diff --git a/.github/main.workflow b/.github/main.workflow new file mode 100644 index 0000000..6533d66 --- /dev/null +++ b/.github/main.workflow @@ -0,0 +1,9 @@ +workflow "CI" { + on = "push" + resolves = ["Build & Lint"] +} + +action "Build & Lint" { + uses = "bwasty/rust-action@master" + args = "cargo build && cargo clippy -- -D warnings -A clippy::ref_in_deref" +} diff --git a/.gitignore b/.gitignore index 53eaa21..cd487fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target **/*.rs.bk +*.spv +.idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d7cde3b..4b345e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,15 +3,31 @@ name = "adler32" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "andrew" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusttype 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "android_glue" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "arrayref" -version = "0.3.5" +name = "approx" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "arrayvec" @@ -22,27 +38,39 @@ dependencies = [ ] [[package]] -name = "bitflags" -version = "1.0.3" +name = "autocfg" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "block" -version = "0.1.6" +name = "backtrace" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "block-buffer" -version = "0.3.3" +name = "backtrace-sys" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "byte-tools" -version = "0.2.0" +name = "bitflags" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -60,6 +88,16 @@ name = "cfg-if" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cgmath" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -78,25 +116,15 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.13.0" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cocoa" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -105,30 +133,13 @@ name = "color_quant" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "core-foundation" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "core-foundation" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation-sys" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -138,46 +149,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "core-graphics" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-graphics" -version = "0.16.0" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-channel" -version = "0.2.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -192,11 +193,11 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.5.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -215,12 +216,25 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -236,8 +250,12 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.5.0" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "deflate" @@ -248,14 +266,6 @@ dependencies = [ "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "digest" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "dlib" version = "0.4.1" @@ -274,11 +284,6 @@ name = "either" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fnv" version = "1.0.6" @@ -297,6 +302,11 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fuchsia-cprng" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -316,14 +326,6 @@ name = "gcc" version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "gif" version = "0.10.0" @@ -333,16 +335,6 @@ dependencies = [ "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "glsl-to-spirv" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "half" version = "1.1.2" @@ -350,19 +342,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "image" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "gif 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -392,7 +384,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.43" +version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -401,7 +393,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "line_drawing" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -431,16 +431,16 @@ name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memmap" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -449,15 +449,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "metal-rs" -version = "0.6.6" +name = "metal" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cocoa 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -466,13 +467,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -487,8 +488,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.14.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -511,7 +512,7 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.1.42" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,7 +529,7 @@ name = "num_cpus" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -566,6 +567,14 @@ dependencies = [ "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ordered-float" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "owning_ref" version = "0.3.3" @@ -576,22 +585,44 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.6.3" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.2.14" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -617,7 +648,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "0.4.13" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -625,32 +656,49 @@ dependencies = [ [[package]] name = "quote" -version = "0.6.6" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.4.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.5.5" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -658,6 +706,75 @@ name = "rand_core" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rayon" version = "1.0.2" @@ -675,21 +792,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "redox_syscall" -version = "0.1.40" +name = "rdrand" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "remove_dir_all" -version = "0.5.1" +name = "rustc-demangle" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rusttype" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "same-file" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -703,14 +847,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "sha2" -version = "0.7.1" +name = "semver" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "shaderc" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -719,7 +874,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -732,19 +887,19 @@ dependencies = [ [[package]] name = "smithay-client-toolkit" -version = "0.2.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "andrew 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-commons 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-protocols 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-protocols 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -752,32 +907,44 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "stb_truetype" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "tempfile" -version = "3.0.3" +name = "syn" +version = "0.15.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "typenum" -version = "1.10.0" +name = "tiff" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "unicode-xid" @@ -799,7 +966,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vk-sys" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -811,101 +978,109 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "vulkan-tutorial-rs" version = "0.1.0" dependencies = [ - "image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vulkano 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vulkano-shader-derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vulkano-win 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winit 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vulkano 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vulkano-shaders 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vulkano-win 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "vulkano" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "half 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vk-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "vk-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "vulkano-shader-derive" -version = "0.10.0" +name = "vulkano-shaders" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glsl-to-spirv 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.8 (registry+https://github.com/rust-lang/crates.io-index)", - "vulkano-shaders 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "shaderc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "vulkano-shaders" -version = "0.10.0" +name = "vulkano-win" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "glsl-to-spirv 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vulkano 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "vulkano-win" -version = "0.10.0" +name = "walkdir" +version = "2.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cocoa 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "metal-rs 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vulkano 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winit 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-client" -version = "0.20.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-commons 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-scanner 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-sys 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", + "downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-commons" -version = "0.20.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-sys 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-protocols" -version = "0.20.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-commons 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-scanner 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-sys 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-scanner" -version = "0.20.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-sys" -version = "0.20.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -914,7 +1089,7 @@ dependencies = [ [[package]] name = "winapi" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -926,6 +1101,14 @@ name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -933,22 +1116,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winit" -version = "0.17.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cocoa 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smithay-client-toolkit 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smithay-client-toolkit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -958,132 +1142,152 @@ version = "2.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "xml-rs" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum andrew 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "142e9e6a99ad0d63a4cf6ce58a4c979f472c5815cbf7e5ca4e47b26a10dc728e" "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c57ff1a5b00753647aebbbcf4ea67fa1e711a65ea7a30eb90dbf07de2485aee" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eff3830839471718ef8522b9025b399bfb713e25bc220da721364efb660d7d" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8389c509ec62b9fe8eca58c502a0acaf017737355615243496cde4994f8fa4f9" "checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "704fbf3bb5149daab0afb255dbea24a1f08d2f4099cedb9baab6d470d4c5eefb" -"checksum cocoa 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac0d785ff4faf0ff23d7b5561346bb50dc7ef9a11cb0e65e07ef776b7752938f" -"checksum cocoa 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5cd1afb83b2de9c41e5dfedb2bcccb779d433b958404876009ae4b01746ff23" +"checksum cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf79daa4e11e5def06e55306aa3601b87de6b5149671529318da048f67cdd77b" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" -"checksum core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8047f547cd6856d45b1cdd75ef8d2f21f3d0e4bf1dab0a0041b0ae9a5dda9c0e" "checksum core-foundation 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cc3532ec724375c7cb7ff0a097b714fde180bb1f6ed2ab27cfcd99ffca873cd2" -"checksum core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "152195421a2e6497a8179195672e9d4ee8e45ed8c465b626f1606d27a08ebcd5" "checksum core-foundation-sys 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a3fb15cdbdd9cf8b82d97d0296bb5cd3631bba58d6e31650a002a8e7fb5721f9" -"checksum core-graphics 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8de78908c558a9ba526877d165635c9eaed0818a785a93efddde1c5bfd2ce5d1" -"checksum core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92801c908ea6301ae619ed842a72e01098085fc321b9c2f3f833dad555bba055" -"checksum crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7408247b1b87f480890f28b670c5f8d9a8a4274833433fe74dc0dfd46d33650" -"checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649" +"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +"checksum crossbeam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1c92ff2d7a202d592f5a412d75cf421495c913817781c1cb383bf12a77e185f" +"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7792c4a9b5a4222f654e3728a3dd945aacc24d2c3a1a096ed265d80e4929cb9a" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" +"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31" -"checksum digest 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b29c278aa8fd30796bd977169e8004b4aa88cdcd2f32a6eb22bc2d5d38df94a" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum gif 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3414b424657317e708489d2857d9575f4403698428b040b609b9d1c1a84a2c" -"checksum glsl-to-spirv 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "605de660ba1c76e66ffb94ef480f5f6b8c00f5a586fc5cebca1af9ac3e92ab08" "checksum half 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a581f551b77eb3e177584e922a8c057e14311a857f859fd39d9574d97d3547da" -"checksum image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebdff791af04e30089bde8ad2a632b86af433b40c04db8d70ad4b21487db7a6a" +"checksum image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44665b4395d1844c96e7dc8ed5754782a1cdfd9ef458a80bbe45702681450504" "checksum inflate 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6f53b811ee8e2057ccf9643ca6b4277de90efaf5e61e55fd5254576926bb4245" "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" -"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" +"checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" "checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54" "checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum metal-rs 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4b77b2a59798a094aa3b06df84ca3618f63ca63abfd6e270a17777fd53f38d6b" -"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" +"checksum metal 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7de9c2b83c946ab01c9942928388f911d93486b97636d9927541345905fea65d" +"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2c31b75c36a993d30c7a13d70513cb93f02acafdd5b7ba250f9b0e18615de7" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" -"checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" +"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9833ab0efe5361b1e2122a0544a5d3359576911a42cb098c2e59be8650807367" "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_exception 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "098cd29a2fa3c230d3463ae069cecccc3fdfd64c0d2496ab5b96f82dab6a00dc" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +"checksum ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0015e9e8e28ee20c581cfbfe47c650cedeb9ed0721090e0b7ebb10b9cdbcc2" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b" -"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" +"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" +"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" +"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "104630aa1c83213cbc76db0703630fcb0421dac3585063be4ce9a8a2feeaa745" "checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b" -"checksum proc-macro2 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ee5697238f0d893c7f0ecc59c0999f18d2af85e424de441178bcacc9f9e6cf67" -"checksum quote 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ed7d650913520df631972f21e104a4fa2f9c82a14afc65d17b388a2e29731e7c" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" +"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "080723c6145e37503a2224f801f252e14ac5531cb450f4502698542d188cb3c0" +"checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa" "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum rusttype 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ae90f66c7ca5fb2c566d373c9ccb3ce1ae1aeebf236b74ad0d413196facb31b3" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum shaderc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a2e5612a0be921f736036765b7d3e778ad80e345329d5ceb96c6b9eb9db0285b" "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" "checksum smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "211a489e65e94b103926d2054ae515a1cdb5d515ea0ef414fee23b7e043ce748" -"checksum smithay-client-toolkit 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2051bffc6cbf271176e8ba1527f801b6444567daee15951ff5152aaaf7777b2f" +"checksum smithay-client-toolkit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d858330eeed4efaf71c560555e2a6a0597d01b7d52685c3cc964ab1cc360f8c6" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" "checksum syn 0.14.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b7bfcbb0c068d0f642a0ffbd5c604965a360a61f99e8add013cef23a838614f3" -"checksum tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4b103c6d08d323b92ff42c8ce62abcd83ca8efa7fd5bf7927efefec75f58c76" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"checksum tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2cc6c4fd13cb1cfd20abdb196e794ceccb29371855b7e7f575945f920a5b3c2" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" -"checksum vk-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9567ee6b79b72dfe50201817a9b903de91a1deb091b41c165c2c3679884d8103" +"checksum vk-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36f5fd4a7d6d5d19808610583131c0aed271556527cad4cb71c436831a28e059" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum vulkano 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9e3da53f262560934db346d11d91d3323f9f0ed9ba18b77e5d1a5d4a8bf8278" -"checksum vulkano-shader-derive 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7bdaad27dedc3cd506d4b58b4bbdd04dce6b5858c04c0e4abec9831e51e5c" -"checksum vulkano-shaders 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f137af3bdb733db9a9a1e5874937037c12ba7fa592485880746e78cd83e41c2" -"checksum vulkano-win 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4d88fcf9015174883872c130565d58611a72e9b55ce45a5fa42981d66656b09" -"checksum wayland-client 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7516a23419a55bd2e6d466c75a6a52c85718e5013660603289c2b8bee794b12" -"checksum wayland-commons 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)" = "d8609d59b95bf198bae4f3b064d55a712f2d529eec6aac98cc1f6e9cc911d47a" -"checksum wayland-protocols 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4d31a96be6ecdbaddbf35200f5af2daee01be592afecd8feaf443d417e9230" -"checksum wayland-scanner 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e674d85ae9c67cbbc590374d8f2e20a7a02fff87ce3a31fc52213afece8d05ad" -"checksum wayland-sys 0.20.12 (registry+https://github.com/rust-lang/crates.io-index)" = "87c82ee658aa657fdfd7061f22e442030d921cfefc5bad68bcf41973e67922f7" -"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum vulkano 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "715b3528d50119f2b7088479a79ff9d95416a6613d18c853d87ad98185ca43d1" +"checksum vulkano-shaders 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce289d8dd668d4c5ba28d1af09e40a09947f138f1cee447f087f771b5ff0030a" +"checksum vulkano-win 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07fffe247f37c8b051e8210ecfb6fe9a13bbb69e05ea61aceb1def5f709320ae" +"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +"checksum wayland-client 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)" = "96041810afa07e7953867d46f8f03c41cbca49ebd1e840eef6abefde8b458b30" +"checksum wayland-commons 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)" = "92af0c5dc724c049e9bd927f8563d9a6abaa94893c5305ef0a6d2805e661f3d3" +"checksum wayland-protocols 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fd94211387fa8ff50df1e4ff7a5529b5a9aebe68ba88acc48e5b7f5fd98f6eef" +"checksum wayland-scanner 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3611f231e675e15c2feb7623103e6449edc6f10b0598cafb3e16e590a0561355" +"checksum wayland-sys 0.21.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2a69d729a1747a5bf40ae05b94c7904b64fbf2381e365c046d872ce4a34aa826" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum winit 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "681133266a330e2df4c76644554d1037401013218ce3293de0c6b795d64ab100" +"checksum winit 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c57c15bd4c0ef18dff33e263e452abe32d00e2e05771cacaa410a14cc1c0776" "checksum x11-dl 2.18.3 (registry+https://github.com/rust-lang/crates.io-index)" = "940586acb859ea05c53971ac231685799a7ec1dee66ac0bccc0e6ad96e06b4e3" -"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" diff --git a/Cargo.toml b/Cargo.toml index f89cbda..a494ae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,16 @@ name = "vulkan-tutorial-rs" version = "0.1.0" authors = ["Benjamin Wasty "] +autobins = true [dependencies] -vulkano = "0.10.0" -vulkano-shader-derive = "0.10.0" -image = "0.19.0" -vulkano-win = "0.10.0" -winit = "0.17.1" +vulkano = "0.11.1" +vulkano-shaders = "0.11.1" +image = "0.20.1" +vulkano-win = "0.11.1" +winit = "0.18.0" +cgmath = "0.17.0" + +# [[bin]] +# name = "main" +# path = "src/bin/00_base_code.rs" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..57bb917 --- /dev/null +++ b/README.md @@ -0,0 +1,376 @@ +# vulkan-tutorial-rs +Rust version of https://github.com/Overv/VulkanTutorial using [Vulkano](http://vulkano.rs/). + +**Goal**: Rust port with code structure as similar as possible to the original C++, so the original tutorial can easily be followed (similar to [learn-opengl-rs](https://github.com/bwasty/learn-opengl-rs)). + +**Current State**: The chapters `Drawing a triangle` and `Vertex buffers` are complete. + +--- +* [Introduction](#introduction) +* [Overview](#overview) +* [Development Environment](#development-environment) +* [Drawing a triangle](#drawing-a-triangle) + * [Setup](#setup) + * [Base code](#base-code) + * [General structure](#general-structure) + * [Resource management](#resource-management) + * [Integrating GLFW winit](#integrating-glfw-winit) + * [Instance](#instance) + * [Validation layers](#validation-layers) + * [Physical devices and queue families](#physical-devices-and-queue-families) + * [Logical device and queues](#logical-device-and-queues) + * [Presentation](#presentation) + * [Window surface](#window-surface) + * [Swap chain](#swap-chain) + * [Image views](#image-views) + * [Graphics pipeline basics](#graphics-pipeline-basics) + * [Introduction](#introduction-1) + * [Shader Modules](#shader-modules) + * [Fixed functions](#fixed-functions) + * [Render passes](#render-passes) + * [Conclusion](#conclusion) + * [Drawing](#drawing) + * [Framebuffers](#framebuffers) + * [Command buffers](#command-buffers) + * [Rendering and presentation](#rendering-and-presentation) + * [Swapchain recreation](#swapchain-recreation) +* [Vertex buffers](#vertex-buffers) + * [Vertex input description](#vertex-input-description) + * [Vertex buffer creation](#vertex-buffer-creation) + * [Staging buffer](#staging-buffer) + * [Index buffer](#index-buffer) +* [Uniform buffers](#uniform-buffers) +* [Texture mapping (TODO)](#texture-mapping-todo) +* [Depth buffering (TODO)](#depth-buffering-todo) +* [Loading models (TODO)](#loading-models-todo) +* [Generating Mipmaps (TODO)](#generating-mipmaps-todo) +* [Multisampling (TODO)](#multisampling-todo) + +## Introduction +This tutorial consists of the the ported code and notes about the differences between the original C++ and the Rust code. +The [explanatory texts](https://vulkan-tutorial.com/Introduction) generally apply equally, although the Rust version is often shorter due to the use of [Vulkano](http://vulkano.rs/), a safe wrapper around the Vulkan API with some convencience functionality (the final triangle example is about 600 lines, compared to 950 lines in C++). + +If you prefer a lower-level API closer to the Vulkan C API, have a look at [Ash](https://github.com/MaikKlein/ash) and [vulkan-tutorial-rust](https://github.com/Usami-Renko/vulkan-tutorial-rust). + +## Overview +https://vulkan-tutorial.com/Overview + +(nothing to note here) + +## Development Environment +https://vulkan-tutorial.com/Development_environment + +Download the Vulkan SDK as described, but ignore everything about library and project setup. Instead, create a new Cargo project: +``` +$ cargo new vulkan-tutorial-rs +``` +Then add this to your `Cargo.toml`: +``` +[dependencies] +vulkano = "0.11.1" +``` + +On macOS, copy [mac-env.sh](mac-env.sh), adapt the `VULKAN_SDK` path if necessary and `source` the file in your terminal. See also [vulkano-rs/vulkano#macos-and-ios-specific-setup](https://github.com/vulkano-rs/vulkano#macos-and-ios-specific-setup). + +## Drawing a triangle +### Setup +#### Base code +https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Base_code +##### General structure +```rust +extern crate vulkano; + +struct HelloTriangleApplication { + +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + Self { + + } + } + + fn main_loop(&mut self) { + + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} +``` + +##### Resource management +Vulkano handles calling `vkDestroyXXX`/`vkFreeXXX` in the `Drop` implementation of all wrapper objects, so we will skip all cleanup code. + +##### Integrating ~GLFW~ winit +Instead of GLFW we'll be using [winit](https://github.com/tomaka/winit), an alternative window managment library written in pure Rust. + +Add this to your Cargo.toml: +``` +winit = "0.18.0" +``` +And extend your main.rs: +```rust +extern crate winit; + +use winit::{WindowBuilder, dpi::LogicalSize}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +struct HelloTriangleApplication { + events_loop: EventsLoop, +} +``` +```rust + pub fn initialize() -> Self { + let events_loop = Self::init_window(); + + Self { + events_loop, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build(&events_loop); + events_loop + } +``` +```rust + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +``` + +[Complete code](src/bin/00_base_code.rs) + + +#### Instance +https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance + +Cargo.toml: +``` +vulkano-win = "0.11.1" +``` +main.rs: +```rust +extern crate vulkano_win; +``` +```rust +use std::sync::Arc; +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, +}; +``` +```rust +struct HelloTriangleApplication { + instance: Option>, + events_loop: EventsLoop, +} +``` +```rust + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let events_loop = Self::init_window(); + + Self { + instance, + events_loop, + } + } +``` +```rust + fn create_instance() -> Arc { + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = vulkano_win::required_extensions(); + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } +``` + +[Diff](src/bin/01_instance_creation.rs.diff) / [Complete code](src/bin/01_instance_creation.rs) + +#### Validation layers +https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers + +From here on we'll just link to the code instead of putting everything in the README: + +[Diff](src/bin/02_validation_layers.rs.diff) / [Complete code](src/bin/02_validation_layers.rs) + + +#### Physical devices and queue families +https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families + +[Diff](src/bin/03_physical_device_selection.rs.diff) / [Complete code](src/bin/03_physical_device_selection.rs) + + +#### Logical device and queues +https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues + +[Diff](src/bin/04_logical_device.rs.diff) / [Complete code](src/bin/04_logical_device.rs) + +### Presentation + +#### Window surface +https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Window_surface + +[Diff](src/bin/05_window_surface.rs.diff) / [Complete code](src/bin/05_window_surface.rs) + +#### Swap chain +https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain + +[Diff](src/bin/06_swap_chain_creation.rs.diff) / [Complete code](src/bin/06_swap_chain_creation.rs) + +#### Image views +https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Image_views + +We're skipping this section because image views are handled by Vulkano and can be accessed via the `SwapchainImage`s created in the last section. + +### Graphics pipeline basics +#### Introduction +https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics + +[Diff](src/bin/08_graphics_pipeline.rs.diff) / [Complete code](src/bin/08_graphics_pipeline.rs) + +#### Shader Modules +https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules + +Instead of compiling the shaders to SPIR-V manually and loading them at runtime, we'll use [vulkano_shaders](https://docs.rs/vulkano-shaders/0.11.1/vulkano_shaders) to do the same at compile-time. Loading them at runtime is also possible, but a bit more invovled - see the [runtime shader](https://github.com/vulkano-rs/vulkano/blob/master/examples/src/bin/runtime-shader/main.rs) example of Vulkano. + +[Diff](src/bin/09_shader_modules.rs.diff) / [Rust code](src/bin/09_shader_modules.rs) / [Vertex shader](src/bin/09_shader_base.vert) / [Fragment shader](src/bin/09_shader_base.frag) + +#### Fixed functions +https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Fixed_functions + +[Diff](src/bin/10_fixed_functions.rs.diff) / [Complete code](src/bin/10_fixed_functions.rs) + +#### Render passes +https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes + +[Diff](src/bin/11_render_passes.rs.diff) / [Complete code](src/bin/11_render_passes.rs) + +#### Conclusion +https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Conclusion + +[Diff](src/bin/12_graphics_pipeline_complete.rs.diff) / [Complete code](src/bin/12_graphics_pipeline_complete.rs) + + +### Drawing +#### Framebuffers +https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Framebuffers + +[Diff](src/bin/13_framebuffers.rs.diff) / [Complete code](src/bin/13_framebuffers.rs) + +#### Command buffers +https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Command_buffers + +We're skipping the first part because Vulkano maintains a [`StandardCommandPool`](https://docs.rs/vulkano/0.10.0/vulkano/command_buffer/pool/standard/struct.StandardCommandPool.html). + +[Diff](src/bin/14_command_buffers.rs.diff) / [Complete code](src/bin/14_command_buffers.rs) + +#### Rendering and presentation +https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation + +[Diff](src/bin/15_hello_triangle.rs.diff) / [Complete code](src/bin/15_hello_triangle.rs) + +### Swapchain recreation +https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation + +[Diff](src/bin/16_swap_chain_recreation.rs.diff) / [Complete code](src/bin/16_swap_chain_recreation.rs) + +## Vertex buffers +### Vertex input description +https://vulkan-tutorial.com/Vertex_buffers/Vertex_input_description + +[Vertex shader diff](src/bin/17_shader_vertexbuffer.vert.diff) / [Vertex shader](src/bin/17_shader_vertexbuffer.vert) + +(Rust code combined with next section, since this alone won't compile) + +### Vertex buffer creation +https://vulkan-tutorial.com/Vertex_buffers/Vertex_buffer_creation + +[Diff](src/bin/18_vertex_buffer.rs.diff) / [Complete code](src/bin/18_vertex_buffer.rs) + +### Staging buffer +https://vulkan-tutorial.com/Vertex_buffers/Staging_buffer + +We're just replacing `CpuAccessibleBuffer` with `ImmutableBuffer`, which uses a staging buffer internally. See [`vulkano::buffer`](https://docs.rs/vulkano/0.10.0/vulkano/buffer/index.html) for an overview of Vulkano's buffer types. + +[Diff](src/bin/19_staging_buffer.rs.diff) / [Complete code](src/bin/19_staging_buffer.rs) + +### Index buffer +https://vulkan-tutorial.com/Vertex_buffers/Index_buffer + +[Diff](src/bin/20_index_buffer.rs.diff) / [Complete code](src/bin/20_index_buffer.rs) + +## Uniform buffers +### Uniform Buffer Object +https://vulkan-tutorial.com/Uniform_buffers + +In this section we change the vertex shader to take a uniform buffer object consisting of a model, view, and projection matrix. +The shader now outputs the final position as the result of multiplying these three matrices with the original vertex position. + +We add a new type of buffer, the CpuAccessibleBuffer, which allows us to update its contents without needing to rebuild +the entire buffer. In order to actually be able to write to this buffer we need to specify its usage as a uniform buffer and +also the destination of a memory transfer. + +Note that unlike the original tutorial we did **not** need to create any layout binding. This is handled internally by vulkano when creating +a descriptor set, as we'll see in the next section. + +At this point our program will compile and run but immediately panic because we specify a binding in our shader but do not +include a matching descriptor set. + +[Vertex Shader Diff](src/bin/21_shader_uniformbuffer.vert.diff) / [Vertex Shader](src/bin/21_shader_uniformbuffer.vert) + +[Diff](src/bin/21_descriptor_layout_and_buffer.rs.diff) / [Complete code](src/bin/21_descriptor_layout_and_buffer.rs) + +### Descriptor Pool and Sets +https://vulkan-tutorial.com/Uniform_buffers/Descriptor_pool_and_sets + +In this section we introduce a new resource, Descriptor Sets, which allow us to specify what buffer resources to transfer to the GPU. +In the last section we made a change to our vertex shader to expect a buffer in binding 0 and descriptor sets allow us to specify +the actual memory that will occupy that binding. + +For each uniform buffer we created in the last section, we create a descriptor set with the buffer bound to it, giving us the same +number of descriptor sets as swap chain images. At the beginning of each frame we now recreate the command buffer, which +includes a new command to copy our updated UniformBufferObject into the respective uniform buffer before the render pass. + +Note that due to the flipping of the Y axis in the projection matrix, we now need to tell vulkan to draw the vertices in +the opposite direction. + +[Diff](src/bin/22_descriptor_pools_and_sets.rs.diff) / [Complete code](src/bin/22_descriptor_pools_and_sets.rs) +## Texture mapping (*TODO*) +## Depth buffering (*TODO*) +## Loading models (*TODO*) +## Generating Mipmaps (*TODO*) +## Multisampling (*TODO*) diff --git a/mac-env.sh b/mac-env.sh old mode 100644 new mode 100755 index ee63fd1..9766cc0 --- a/mac-env.sh +++ b/mac-env.sh @@ -1,5 +1,6 @@ #!/bin/bash -export VULKAN_SDK=$HOME/Downloads/vulkansdk-macos-1.1.82.0/macOS +export VULKAN_SDK=$HOME/vulkansdk-macos-1.1.97.0/macOS export PATH=$VULKAN_SDK/bin:$PATH export DYLD_LIBRARY_PATH=$VULKAN_SDK/lib:$DYLD_LIBRARY_PATH export VK_ICD_FILENAMES=$VULKAN_SDK/etc/vulkan/icd.d/MoltenVK_icd.json +export VK_LAYER_PATH=$VULKAN_SDK/etc/vulkan/explicit_layer.d diff --git a/src/bin/00_base_code.rs b/src/bin/00_base_code.rs new file mode 100644 index 0000000..dcd5ed7 --- /dev/null +++ b/src/bin/00_base_code.rs @@ -0,0 +1,50 @@ +extern crate vulkano; +extern crate winit; + +use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +#[allow(unused)] +struct HelloTriangleApplication { + events_loop: EventsLoop, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let events_loop = Self::init_window(); + + Self { + events_loop, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build(&events_loop); + events_loop + } + + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/01_instance_creation.rs b/src/bin/01_instance_creation.rs new file mode 100644 index 0000000..a00d39c --- /dev/null +++ b/src/bin/01_instance_creation.rs @@ -0,0 +1,81 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; + +use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + events_loop: EventsLoop, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let events_loop = Self::init_window(); + + Self { + instance, + events_loop, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window_builder = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); + // .build(&self.events_loop.as_ref().unwrap()); + events_loop + } + + fn create_instance() -> Arc { + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = vulkano_win::required_extensions(); + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/01_instance_creation.rs.diff b/src/bin/01_instance_creation.rs.diff new file mode 100644 index 0000000..445a8e0 --- /dev/null +++ b/src/bin/01_instance_creation.rs.diff @@ -0,0 +1,80 @@ +--- a/00_base_code.rs ++++ b/01_instance_creation.rs +@@ -1,34 +1,65 @@ + extern crate vulkano; ++extern crate vulkano_win; + extern crate winit; + ++use std::sync::Arc; ++ + use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + ++use vulkano::instance::{ ++ Instance, ++ InstanceExtensions, ++ ApplicationInfo, ++ Version, ++}; ++ + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; + + #[allow(unused)] + struct HelloTriangleApplication { ++ instance: Arc, + events_loop: EventsLoop, + } + + impl HelloTriangleApplication { + pub fn initialize() -> Self { ++ let instance = Self::create_instance(); + let events_loop = Self::init_window(); + + Self { ++ instance, + events_loop, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); +- let _window = WindowBuilder::new() ++ let _window_builder = WindowBuilder::new() + .with_title("Vulkan") +- .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) +- .build(&events_loop); ++ .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); ++ // .build(&self.events_loop.as_ref().unwrap()); + events_loop + } + ++ fn create_instance() -> Arc { ++ let supported_extensions = InstanceExtensions::supported_by_core() ++ .expect("failed to retrieve supported extensions"); ++ println!("Supported extensions: {:?}", supported_extensions); ++ ++ let app_info = ApplicationInfo { ++ application_name: Some("Hello Triangle".into()), ++ application_version: Some(Version { major: 1, minor: 0, patch: 0 }), ++ engine_name: Some("No Engine".into()), ++ engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), ++ }; ++ ++ let required_extensions = vulkano_win::required_extensions(); ++ Instance::new(Some(&app_info), &required_extensions, None) ++ .expect("failed to create Vulkan instance") ++ } ++ ++ #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; +@@ -45,6 +76,6 @@ impl HelloTriangleApplication { + } + + fn main() { +- let mut app = HelloTriangleApplication::initialize(); +- app.main_loop(); ++ let mut _app = HelloTriangleApplication::initialize(); ++ // app.main_loop(); + } diff --git a/src/bin/02_validation_layers.rs b/src/bin/02_validation_layers.rs new file mode 100644 index 0000000..2cc4024 --- /dev/null +++ b/src/bin/02_validation_layers.rs @@ -0,0 +1,141 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; + +use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + + let events_loop = Self::init_window(); + + Self { + instance, + debug_callback, + + events_loop, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window_builder = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); + // .build(&self.events_loop.as_ref().unwrap()); + events_loop + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/02_validation_layers.rs.diff b/src/bin/02_validation_layers.rs.diff new file mode 100644 index 0000000..bbf3b43 --- /dev/null +++ b/src/bin/02_validation_layers.rs.diff @@ -0,0 +1,107 @@ +--- a/01_instance_creation.rs ++++ b/02_validation_layers.rs +@@ -11,24 +11,41 @@ use vulkano::instance::{ + InstanceExtensions, + ApplicationInfo, + Version, ++ layers_list, + }; ++use vulkano::instance::debug::{DebugCallback, MessageTypes}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; + ++const VALIDATION_LAYERS: &[&str] = &[ ++ "VK_LAYER_LUNARG_standard_validation" ++]; ++ ++#[cfg(all(debug_assertions))] ++const ENABLE_VALIDATION_LAYERS: bool = true; ++#[cfg(not(debug_assertions))] ++const ENABLE_VALIDATION_LAYERS: bool = false; ++ + #[allow(unused)] + struct HelloTriangleApplication { + instance: Arc, ++ debug_callback: Option, ++ + events_loop: EventsLoop, + } + + impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); ++ let debug_callback = Self::setup_debug_callback(&instance); ++ + let events_loop = Self::init_window(); + + Self { + instance, ++ debug_callback, ++ + events_loop, + } + } +@@ -43,6 +60,10 @@ impl HelloTriangleApplication { + } + + fn create_instance() -> Arc { ++ if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { ++ println!("Validation layers requested, but not available!") ++ } ++ + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); +@@ -54,9 +75,48 @@ impl HelloTriangleApplication { + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + +- let required_extensions = vulkano_win::required_extensions(); +- Instance::new(Some(&app_info), &required_extensions, None) +- .expect("failed to create Vulkan instance") ++ let required_extensions = Self::get_required_extensions(); ++ ++ if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { ++ Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) ++ .expect("failed to create Vulkan instance") ++ } else { ++ Instance::new(Some(&app_info), &required_extensions, None) ++ .expect("failed to create Vulkan instance") ++ } ++ } ++ ++ fn check_validation_layer_support() -> bool { ++ let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); ++ VALIDATION_LAYERS.iter() ++ .all(|layer_name| layers.contains(&layer_name.to_string())) ++ } ++ ++ fn get_required_extensions() -> InstanceExtensions { ++ let mut extensions = vulkano_win::required_extensions(); ++ if ENABLE_VALIDATION_LAYERS { ++ // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano ++ extensions.ext_debug_report = true; ++ } ++ ++ extensions ++ } ++ ++ fn setup_debug_callback(instance: &Arc) -> Option { ++ if !ENABLE_VALIDATION_LAYERS { ++ return None; ++ } ++ ++ let msg_types = MessageTypes { ++ error: true, ++ warning: true, ++ performance_warning: true, ++ information: false, ++ debug: true, ++ }; ++ DebugCallback::new(&instance, msg_types, |msg| { ++ println!("validation layer: {:?}", msg.description); ++ }).ok() + } + + #[allow(unused)] diff --git a/src/bin/03_physical_device_selection.rs b/src/bin/03_physical_device_selection.rs new file mode 100644 index 0000000..4456a44 --- /dev/null +++ b/src/bin/03_physical_device_selection.rs @@ -0,0 +1,188 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; + +use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + + let events_loop = Self::init_window(); + + let physical_device_index = Self::pick_physical_device(&instance); + + Self { + instance, + debug_callback, + + events_loop, + + physical_device_index, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window_builder = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); + // .build(&self.events_loop.as_ref().unwrap()); + events_loop + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(&device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(device); + indices.is_complete() + } + + fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/03_physical_device_selection.rs.diff b/src/bin/03_physical_device_selection.rs.diff new file mode 100644 index 0000000..eee3881 --- /dev/null +++ b/src/bin/03_physical_device_selection.rs.diff @@ -0,0 +1,88 @@ +--- a/02_validation_layers.rs ++++ b/03_physical_device_selection.rs +@@ -12,6 +12,7 @@ use vulkano::instance::{ + ApplicationInfo, + Version, + layers_list, ++ PhysicalDevice, + }; + use vulkano::instance::debug::{DebugCallback, MessageTypes}; + +@@ -27,12 +28,27 @@ const ENABLE_VALIDATION_LAYERS: bool = true; + #[cfg(not(debug_assertions))] + const ENABLE_VALIDATION_LAYERS: bool = false; + ++struct QueueFamilyIndices { ++ graphics_family: i32, ++} ++impl QueueFamilyIndices { ++ fn new() -> Self { ++ Self { graphics_family: -1 } ++ } ++ ++ fn is_complete(&self) -> bool { ++ self.graphics_family >= 0 ++ } ++} ++ + #[allow(unused)] + struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, ++ ++ physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + } + + impl HelloTriangleApplication { +@@ -42,11 +58,15 @@ impl HelloTriangleApplication { + + let events_loop = Self::init_window(); + ++ let physical_device_index = Self::pick_physical_device(&instance); ++ + Self { + instance, + debug_callback, + + events_loop, ++ ++ physical_device_index, + } + } + +@@ -119,6 +139,33 @@ impl HelloTriangleApplication { + }).ok() + } + ++ fn pick_physical_device(instance: &Arc) -> usize { ++ PhysicalDevice::enumerate(&instance) ++ .position(|device| Self::is_device_suitable(&device)) ++ .expect("failed to find a suitable GPU!") ++ } ++ ++ fn is_device_suitable(device: &PhysicalDevice) -> bool { ++ let indices = Self::find_queue_families(device); ++ indices.is_complete() ++ } ++ ++ fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { ++ let mut indices = QueueFamilyIndices::new(); ++ // TODO: replace index with id to simplify? ++ for (i, queue_family) in device.queue_families().enumerate() { ++ if queue_family.supports_graphics() { ++ indices.graphics_family = i as i32; ++ } ++ ++ if indices.is_complete() { ++ break; ++ } ++ } ++ ++ indices ++ } ++ + #[allow(unused)] + fn main_loop(&mut self) { + loop { diff --git a/src/bin/04_logical_device.rs b/src/bin/04_logical_device.rs new file mode 100644 index 0000000..c74b0fe --- /dev/null +++ b/src/bin/04_logical_device.rs @@ -0,0 +1,222 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; + +use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + + let events_loop = Self::init_window(); + + let physical_device_index = Self::pick_physical_device(&instance); + let (device, graphics_queue) = Self::create_logical_device( + &instance, physical_device_index); + + Self { + instance, + debug_callback, + + events_loop, + + physical_device_index, + device, + + graphics_queue, + } + } + + fn init_window() -> EventsLoop { + let events_loop = EventsLoop::new(); + let _window_builder = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); + // .build(&self.events_loop.as_ref().unwrap()); + events_loop + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(&device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(device); + indices.is_complete() + } + + fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + physical_device_index: usize, + ) -> (Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&physical_device); + + let queue_family = physical_device.queue_families() + .nth(indices.graphics_family as usize).unwrap(); + + let queue_priority = 1.0; + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), + [(queue_family, queue_priority)].iter().cloned()) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + + (device, graphics_queue) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/04_logical_device.rs.diff b/src/bin/04_logical_device.rs.diff new file mode 100644 index 0000000..bfebbc8 --- /dev/null +++ b/src/bin/04_logical_device.rs.diff @@ -0,0 +1,71 @@ +--- a/03_physical_device_selection.rs ++++ b/04_logical_device.rs +@@ -15,6 +15,7 @@ use vulkano::instance::{ + PhysicalDevice, + }; + use vulkano::instance::debug::{DebugCallback, MessageTypes}; ++use vulkano::device::{Device, DeviceExtensions, Queue, Features}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -49,6 +50,9 @@ struct HelloTriangleApplication { + events_loop: EventsLoop, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) ++ device: Arc, ++ ++ graphics_queue: Arc, + } + + impl HelloTriangleApplication { +@@ -59,6 +63,8 @@ impl HelloTriangleApplication { + let events_loop = Self::init_window(); + + let physical_device_index = Self::pick_physical_device(&instance); ++ let (device, graphics_queue) = Self::create_logical_device( ++ &instance, physical_device_index); + + Self { + instance, +@@ -67,6 +73,9 @@ impl HelloTriangleApplication { + events_loop, + + physical_device_index, ++ device, ++ ++ graphics_queue, + } + } + +@@ -166,6 +175,31 @@ impl HelloTriangleApplication { + indices + } + ++ fn create_logical_device( ++ instance: &Arc, ++ physical_device_index: usize, ++ ) -> (Arc, Arc) { ++ let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); ++ let indices = Self::find_queue_families(&physical_device); ++ ++ let queue_family = physical_device.queue_families() ++ .nth(indices.graphics_family as usize).unwrap(); ++ ++ let queue_priority = 1.0; ++ ++ // NOTE: the tutorial recommends passing the validation layers as well ++ // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that ++ // for us internally. ++ ++ let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), ++ [(queue_family, queue_priority)].iter().cloned()) ++ .expect("failed to create logical device!"); ++ ++ let graphics_queue = queues.next().unwrap(); ++ ++ (device, graphics_queue) ++ } ++ + #[allow(unused)] + fn main_loop(&mut self) { + loop { diff --git a/src/bin/05_window_surface.rs b/src/bin/05_window_surface.rs new file mode 100644 index 0000000..b02d00c --- /dev/null +++ b/src/bin/05_window_surface.rs @@ -0,0 +1,242 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + indices.is_complete() + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &DeviceExtensions::none(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/05_window_surface.rs.diff b/src/bin/05_window_surface.rs.diff new file mode 100644 index 0000000..72b1cd8 --- /dev/null +++ b/src/bin/05_window_surface.rs.diff @@ -0,0 +1,183 @@ +--- a/04_logical_device.rs ++++ b/05_window_surface.rs +@@ -3,8 +3,10 @@ extern crate vulkano_win; + extern crate winit; + + use std::sync::Arc; ++use std::collections::HashSet; + +-use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; ++use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; ++use vulkano_win::VkSurfaceBuild; + + use vulkano::instance::{ + Instance, +@@ -16,6 +18,9 @@ use vulkano::instance::{ + }; + use vulkano::instance::debug::{DebugCallback, MessageTypes}; + use vulkano::device::{Device, DeviceExtensions, Queue, Features}; ++use vulkano::swapchain::{ ++ Surface, ++}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -31,14 +36,15 @@ const ENABLE_VALIDATION_LAYERS: bool = false; + + struct QueueFamilyIndices { + graphics_family: i32, ++ present_family: i32, + } + impl QueueFamilyIndices { + fn new() -> Self { +- Self { graphics_family: -1 } ++ Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { +- self.graphics_family >= 0 ++ self.graphics_family >= 0 && self.present_family >= 0 + } + } + +@@ -48,46 +54,40 @@ struct HelloTriangleApplication { + debug_callback: Option, + + events_loop: EventsLoop, ++ surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, ++ present_queue: Arc, + } + + impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); ++ let (events_loop, surface) = Self::create_surface(&instance); + +- let events_loop = Self::init_window(); +- +- let physical_device_index = Self::pick_physical_device(&instance); +- let (device, graphics_queue) = Self::create_logical_device( +- &instance, physical_device_index); ++ let physical_device_index = Self::pick_physical_device(&instance, &surface); ++ let (device, graphics_queue, present_queue) = Self::create_logical_device( ++ &instance, &surface, physical_device_index); + + Self { + instance, + debug_callback, + + events_loop, ++ surface, + + physical_device_index, + device, + + graphics_queue, ++ present_queue, + } + } + +- fn init_window() -> EventsLoop { +- let events_loop = EventsLoop::new(); +- let _window_builder = WindowBuilder::new() +- .with_title("Vulkan") +- .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); +- // .build(&self.events_loop.as_ref().unwrap()); +- events_loop +- } +- + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") +@@ -148,18 +148,18 @@ impl HelloTriangleApplication { + }).ok() + } + +- fn pick_physical_device(instance: &Arc) -> usize { ++ fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) +- .position(|device| Self::is_device_suitable(&device)) ++ .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + +- fn is_device_suitable(device: &PhysicalDevice) -> bool { +- let indices = Self::find_queue_families(device); ++ fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { ++ let indices = Self::find_queue_families(surface, device); + indices.is_complete() + } + +- fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { ++ fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { +@@ -167,6 +167,10 @@ impl HelloTriangleApplication { + indices.graphics_family = i as i32; + } + ++ if surface.is_supported(queue_family).unwrap() { ++ indices.present_family = i as i32; ++ } ++ + if indices.is_complete() { + break; + } +@@ -177,27 +181,43 @@ impl HelloTriangleApplication { + + fn create_logical_device( + instance: &Arc, ++ surface: &Arc>, + physical_device_index: usize, +- ) -> (Arc, Arc) { ++ ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); +- let indices = Self::find_queue_families(&physical_device); ++ let indices = Self::find_queue_families(&surface, &physical_device); + +- let queue_family = physical_device.queue_families() +- .nth(indices.graphics_family as usize).unwrap(); ++ let families = [indices.graphics_family, indices.present_family]; ++ use std::iter::FromIterator; ++ let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; ++ let queue_families = unique_queue_families.iter().map(|i| { ++ (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) ++ }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + +- let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), +- [(queue_family, queue_priority)].iter().cloned()) ++ let (device, mut queues) = Device::new(physical_device, &Features::none(), ++ &DeviceExtensions::none(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); ++ let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); ++ ++ (device, graphics_queue, present_queue) ++ } + +- (device, graphics_queue) ++ fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { ++ let events_loop = EventsLoop::new(); ++ let surface = WindowBuilder::new() ++ .with_title("Vulkan") ++ .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) ++ .build_vk_surface(&events_loop, instance.clone()) ++ .expect("failed to create window surface!"); ++ (events_loop, surface) + } + + #[allow(unused)] diff --git a/src/bin/06_swap_chain_creation.rs b/src/bin/06_swap_chain_creation.rs new file mode 100644 index 0000000..044566d --- /dev/null +++ b/src/bin/06_swap_chain_creation.rs @@ -0,0 +1,371 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/06_swap_chain_creation.rs.diff b/src/bin/06_swap_chain_creation.rs.diff new file mode 100644 index 0000000..525e0c6 --- /dev/null +++ b/src/bin/06_swap_chain_creation.rs.diff @@ -0,0 +1,185 @@ +--- a/05_window_surface.rs ++++ b/06_swap_chain_creation.rs +@@ -20,7 +20,16 @@ use vulkano::instance::debug::{DebugCallback, MessageTypes}; + use vulkano::device::{Device, DeviceExtensions, Queue, Features}; + use vulkano::swapchain::{ + Surface, ++ Capabilities, ++ ColorSpace, ++ SupportedPresentModes, ++ PresentMode, ++ Swapchain, ++ CompositeAlpha, + }; ++use vulkano::format::Format; ++use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; ++use vulkano::sync::SharingMode; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -29,6 +38,14 @@ const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" + ]; + ++/// Required device extensions ++fn device_extensions() -> DeviceExtensions { ++ DeviceExtensions { ++ khr_swapchain: true, ++ .. vulkano::device::DeviceExtensions::none() ++ } ++} ++ + #[cfg(all(debug_assertions))] + const ENABLE_VALIDATION_LAYERS: bool = true; + #[cfg(not(debug_assertions))] +@@ -61,6 +78,9 @@ struct HelloTriangleApplication { + + graphics_queue: Arc, + present_queue: Arc, ++ ++ swap_chain: Arc>, ++ swap_chain_images: Vec>>, + } + + impl HelloTriangleApplication { +@@ -73,6 +93,9 @@ impl HelloTriangleApplication { + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + ++ let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, ++ &device, &graphics_queue, &present_queue); ++ + Self { + instance, + debug_callback, +@@ -85,6 +108,9 @@ impl HelloTriangleApplication { + + graphics_queue, + present_queue, ++ ++ swap_chain, ++ swap_chain_images, + } + } + +@@ -156,7 +182,110 @@ impl HelloTriangleApplication { + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); +- indices.is_complete() ++ let extensions_supported = Self::check_device_extension_support(device); ++ ++ let swap_chain_adequate = if extensions_supported { ++ let capabilities = surface.capabilities(*device) ++ .expect("failed to get surface capabilities"); ++ !capabilities.supported_formats.is_empty() && ++ capabilities.present_modes.iter().next().is_some() ++ } else { ++ false ++ }; ++ ++ indices.is_complete() && extensions_supported && swap_chain_adequate ++ } ++ ++ fn check_device_extension_support(device: &PhysicalDevice) -> bool { ++ let available_extensions = DeviceExtensions::supported_by_device(*device); ++ let device_extensions = device_extensions(); ++ available_extensions.intersection(&device_extensions) == device_extensions ++ } ++ ++ fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { ++ // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be ++ // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) ++ *available_formats.iter() ++ .find(|(format, color_space)| ++ *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear ++ ) ++ .unwrap_or_else(|| &available_formats[0]) ++ } ++ ++ fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { ++ if available_present_modes.mailbox { ++ PresentMode::Mailbox ++ } else if available_present_modes.immediate { ++ PresentMode::Immediate ++ } else { ++ PresentMode::Fifo ++ } ++ } ++ ++ fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { ++ if let Some(current_extent) = capabilities.current_extent { ++ return current_extent ++ } else { ++ let mut actual_extent = [WIDTH, HEIGHT]; ++ actual_extent[0] = capabilities.min_image_extent[0] ++ .max(capabilities.max_image_extent[0].min(actual_extent[0])); ++ actual_extent[1] = capabilities.min_image_extent[1] ++ .max(capabilities.max_image_extent[1].min(actual_extent[1])); ++ actual_extent ++ } ++ } ++ ++ fn create_swap_chain( ++ instance: &Arc, ++ surface: &Arc>, ++ physical_device_index: usize, ++ device: &Arc, ++ graphics_queue: &Arc, ++ present_queue: &Arc, ++ ) -> (Arc>, Vec>>) { ++ let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); ++ let capabilities = surface.capabilities(physical_device) ++ .expect("failed to get surface capabilities"); ++ ++ let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); ++ let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); ++ let extent = Self::choose_swap_extent(&capabilities); ++ ++ let mut image_count = capabilities.min_image_count + 1; ++ if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { ++ image_count = capabilities.max_image_count.unwrap(); ++ } ++ ++ let image_usage = ImageUsage { ++ color_attachment: true, ++ .. ImageUsage::none() ++ }; ++ ++ let indices = Self::find_queue_families(&surface, &physical_device); ++ ++ let sharing: SharingMode = if indices.graphics_family != indices.present_family { ++ vec![graphics_queue, present_queue].as_slice().into() ++ } else { ++ graphics_queue.into() ++ }; ++ ++ let (swap_chain, images) = Swapchain::new( ++ device.clone(), ++ surface.clone(), ++ image_count, ++ surface_format.0, // TODO: color space? ++ extent, ++ 1, // layers ++ image_usage, ++ sharing, ++ capabilities.current_transform, ++ CompositeAlpha::Opaque, ++ present_mode, ++ true, // clipped ++ None, ++ ).expect("failed to create swap chain!"); ++ ++ (swap_chain, images) + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { +@@ -201,7 +330,7 @@ impl HelloTriangleApplication { + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), +- &DeviceExtensions::none(), queue_families) ++ &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); diff --git a/src/bin/08_graphics_pipeline.rs b/src/bin/08_graphics_pipeline.rs new file mode 100644 index 0000000..a30b410 --- /dev/null +++ b/src/bin/08_graphics_pipeline.rs @@ -0,0 +1,377 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + Self::create_graphics_pipeline(&device); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_graphics_pipeline(_device: &Arc) { + + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/08_graphics_pipeline.rs.diff b/src/bin/08_graphics_pipeline.rs.diff new file mode 100644 index 0000000..b748cf5 --- /dev/null +++ b/src/bin/08_graphics_pipeline.rs.diff @@ -0,0 +1,22 @@ +--- a/06_swap_chain_creation.rs ++++ b/08_graphics_pipeline.rs +@@ -96,6 +96,8 @@ impl HelloTriangleApplication { + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + ++ Self::create_graphics_pipeline(&device); ++ + Self { + instance, + debug_callback, +@@ -288,6 +290,10 @@ impl HelloTriangleApplication { + (swap_chain, images) + } + ++ fn create_graphics_pipeline(_device: &Arc) { ++ ++ } ++ + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? diff --git a/src/bin/09_shader_base.frag b/src/bin/09_shader_base.frag new file mode 100644 index 0000000..84daf5e --- /dev/null +++ b/src/bin/09_shader_base.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/src/bin/09_shader_base.vert b/src/bin/09_shader_base.vert new file mode 100644 index 0000000..d38f815 --- /dev/null +++ b/src/bin/09_shader_base.vert @@ -0,0 +1,25 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +out gl_PerVertex { + vec4 gl_Position; +}; + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} diff --git a/src/bin/09_shader_modules.rs b/src/bin/09_shader_modules.rs new file mode 100644 index 0000000..29da4dc --- /dev/null +++ b/src/bin/09_shader_modules.rs @@ -0,0 +1,396 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + Self::create_graphics_pipeline(&device); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_graphics_pipeline( + device: &Arc, + ) { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let _vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let _frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/10_fixed_functions.rs b/src/bin/10_fixed_functions.rs new file mode 100644 index 0000000..a86a3e3 --- /dev/null +++ b/src/bin/10_fixed_functions.rs @@ -0,0 +1,426 @@ +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + viewport::Viewport, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + Self::create_graphics_pipeline(&device, swap_chain.dimensions()); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + ) { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + let _pipeline_builder = Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + ); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/10_fixed_functions.rs.diff b/src/bin/10_fixed_functions.rs.diff new file mode 100644 index 0000000..f798e53 --- /dev/null +++ b/src/bin/10_fixed_functions.rs.diff @@ -0,0 +1,77 @@ +--- a/08_graphics_pipeline.rs ++++ b/10_fixed_functions.rs +@@ -30,6 +30,11 @@ use vulkano::swapchain::{ + use vulkano::format::Format; + use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; + use vulkano::sync::SharingMode; ++use vulkano::pipeline::{ ++ GraphicsPipeline, ++ vertex::BufferlessDefinition, ++ viewport::Viewport, ++}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -96,7 +101,7 @@ impl HelloTriangleApplication { + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + +- Self::create_graphics_pipeline(&device); ++ Self::create_graphics_pipeline(&device, swap_chain.dimensions()); + + Self { + instance, +@@ -290,8 +295,52 @@ impl HelloTriangleApplication { + (swap_chain, images) + } + +- fn create_graphics_pipeline(_device: &Arc) { ++ fn create_graphics_pipeline( ++ device: &Arc, ++ swap_chain_extent: [u32; 2], ++ ) { ++ mod vertex_shader { ++ vulkano_shaders::shader! { ++ ty: "vertex", ++ path: "src/bin/09_shader_base.vert" ++ } ++ } ++ ++ mod fragment_shader { ++ vulkano_shaders::shader! { ++ ty: "fragment", ++ path: "src/bin/09_shader_base.frag" ++ } ++ } ++ ++ let vert_shader_module = vertex_shader::Shader::load(device.clone()) ++ .expect("failed to create vertex shader module!"); ++ let frag_shader_module = fragment_shader::Shader::load(device.clone()) ++ .expect("failed to create fragment shader module!"); ++ ++ let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; ++ let viewport = Viewport { ++ origin: [0.0, 0.0], ++ dimensions, ++ depth_range: 0.0 .. 1.0, ++ }; + ++ let _pipeline_builder = Arc::new(GraphicsPipeline::start() ++ .vertex_input(BufferlessDefinition {}) ++ .vertex_shader(vert_shader_module.main_entry_point(), ()) ++ .triangle_list() ++ .primitive_restart(false) ++ .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport ++ .fragment_shader(frag_shader_module.main_entry_point(), ()) ++ .depth_clamp(false) ++ // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... ++ .polygon_mode_fill() // = default ++ .line_width(1.0) // = default ++ .cull_mode_back() ++ .front_face_clockwise() ++ // NOTE: no depth_bias here, but on pipeline::raster::Rasterization ++ .blend_pass_through() // = default ++ ); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { diff --git a/src/bin/11_render_passes.rs b/src/bin/11_render_passes.rs new file mode 100644 index 0000000..5cac78e --- /dev/null +++ b/src/bin/11_render_passes.rs @@ -0,0 +1,452 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + Self::create_graphics_pipeline(&device, swap_chain.dimensions()); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + ) { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + let _pipeline_builder = Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + ); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/11_render_passes.rs.diff b/src/bin/11_render_passes.rs.diff new file mode 100644 index 0000000..6dd7f4e --- /dev/null +++ b/src/bin/11_render_passes.rs.diff @@ -0,0 +1,67 @@ +--- a/10_fixed_functions.rs ++++ b/11_render_passes.rs +@@ -1,3 +1,4 @@ ++#[macro_use] + extern crate vulkano; + extern crate vulkano_win; + extern crate winit; +@@ -35,6 +36,9 @@ use vulkano::pipeline::{ + vertex::BufferlessDefinition, + viewport::Viewport, + }; ++use vulkano::framebuffer::{ ++ RenderPassAbstract, ++}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -86,6 +90,8 @@ struct HelloTriangleApplication { + + swap_chain: Arc>, + swap_chain_images: Vec>>, ++ ++ render_pass: Arc, + } + + impl HelloTriangleApplication { +@@ -101,6 +107,7 @@ impl HelloTriangleApplication { + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + ++ let render_pass = Self::create_render_pass(&device, swap_chain.format()); + Self::create_graphics_pipeline(&device, swap_chain.dimensions()); + + Self { +@@ -118,6 +125,8 @@ impl HelloTriangleApplication { + + swap_chain, + swap_chain_images, ++ ++ render_pass, + } + } + +@@ -295,6 +304,23 @@ impl HelloTriangleApplication { + (swap_chain, images) + } + ++ fn create_render_pass(device: &Arc, color_format: Format) -> Arc { ++ Arc::new(single_pass_renderpass!(device.clone(), ++ attachments: { ++ color: { ++ load: Clear, ++ store: Store, ++ format: color_format, ++ samples: 1, ++ } ++ }, ++ pass: { ++ color: [color], ++ depth_stencil: {} ++ } ++ ).unwrap()) ++ } ++ + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], diff --git a/src/bin/12_graphics_pipeline_complete.rs b/src/bin/12_graphics_pipeline_complete.rs new file mode 100644 index 0000000..41795be --- /dev/null +++ b/src/bin/12_graphics_pipeline_complete.rs @@ -0,0 +1,466 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, +}; +use vulkano::descriptor::PipelineLayoutAbstract; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + // NOTE: We need to the full type of + // self.graphics_pipeline, because `BufferlessVertices` only + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/12_graphics_pipeline_complete.rs.diff b/src/bin/12_graphics_pipeline_complete.rs.diff new file mode 100644 index 0000000..f231198 --- /dev/null +++ b/src/bin/12_graphics_pipeline_complete.rs.diff @@ -0,0 +1,81 @@ +--- a/11_render_passes.rs ++++ b/12_graphics_pipeline_complete.rs +@@ -38,7 +38,9 @@ use vulkano::pipeline::{ + }; + use vulkano::framebuffer::{ + RenderPassAbstract, ++ Subpass, + }; ++use vulkano::descriptor::PipelineLayoutAbstract; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -74,6 +76,8 @@ impl QueueFamilyIndices { + } + } + ++type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; ++ + #[allow(unused)] + struct HelloTriangleApplication { + instance: Arc, +@@ -92,6 +96,11 @@ struct HelloTriangleApplication { + swap_chain_images: Vec>>, + + render_pass: Arc, ++ // NOTE: We need to the full type of ++ // self.graphics_pipeline, because `BufferlessVertices` only ++ // works when the concrete type of the graphics pipeline is visible ++ // to the command buffer. ++ graphics_pipeline: Arc, + } + + impl HelloTriangleApplication { +@@ -108,7 +117,7 @@ impl HelloTriangleApplication { + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); +- Self::create_graphics_pipeline(&device, swap_chain.dimensions()); ++ let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + Self { + instance, +@@ -127,6 +136,7 @@ impl HelloTriangleApplication { + swap_chain_images, + + render_pass, ++ graphics_pipeline, + } + } + +@@ -324,7 +334,8 @@ impl HelloTriangleApplication { + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], +- ) { ++ render_pass: &Arc, ++ ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", +@@ -351,7 +362,7 @@ impl HelloTriangleApplication { + depth_range: 0.0 .. 1.0, + }; + +- let _pipeline_builder = Arc::new(GraphicsPipeline::start() ++ Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() +@@ -366,7 +377,10 @@ impl HelloTriangleApplication { + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default +- ); ++ .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) ++ .build(device.clone()) ++ .unwrap() ++ ) + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { diff --git a/src/bin/13_framebuffers.rs b/src/bin/13_framebuffers.rs new file mode 100644 index 0000000..1de5b6b --- /dev/null +++ b/src/bin/13_framebuffers.rs @@ -0,0 +1,488 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::descriptor::PipelineLayoutAbstract; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + // NOTE: We need to the full type of + // self.graphics_pipeline, because `BufferlessVertices` only + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + } + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/13_framebuffers.rs.diff b/src/bin/13_framebuffers.rs.diff new file mode 100644 index 0000000..4cf0506 --- /dev/null +++ b/src/bin/13_framebuffers.rs.diff @@ -0,0 +1,59 @@ +--- a/12_graphics_pipeline_complete.rs ++++ b/13_framebuffers.rs +@@ -39,6 +39,8 @@ use vulkano::pipeline::{ + use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, ++ FramebufferAbstract, ++ Framebuffer, + }; + use vulkano::descriptor::PipelineLayoutAbstract; + +@@ -101,6 +103,8 @@ struct HelloTriangleApplication { + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, ++ ++ swap_chain_framebuffers: Vec>, + } + + impl HelloTriangleApplication { +@@ -119,6 +123,8 @@ impl HelloTriangleApplication { + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + ++ let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); ++ + Self { + instance, + debug_callback, +@@ -137,6 +143,8 @@ impl HelloTriangleApplication { + + render_pass, + graphics_pipeline, ++ ++ swap_chain_framebuffers, + } + } + +@@ -383,6 +391,20 @@ impl HelloTriangleApplication { + ) + } + ++ fn create_framebuffers( ++ swap_chain_images: &[Arc>], ++ render_pass: &Arc ++ ) -> Vec> { ++ swap_chain_images.iter() ++ .map(|image| { ++ let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) ++ .add(image.clone()).unwrap() ++ .build().unwrap()); ++ fba ++ } ++ ).collect::>() ++ } ++ + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? diff --git a/src/bin/14_command_buffers.rs b/src/bin/14_command_buffers.rs new file mode 100644 index 0000000..f2b2144 --- /dev/null +++ b/src/bin/14_command_buffers.rs @@ -0,0 +1,521 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::SharingMode; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + vertex::BufferlessVertices, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::descriptor::PipelineLayoutAbstract; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + // NOTE: We need to the full type of + // self.graphics_pipeline, because `BufferlessVertices` only + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + command_buffers: Vec>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + command_buffers: vec![], + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + let vertices = BufferlessVertices { vertices: 3, instances: 1 }; + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), + vertices, (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } +} + +fn main() { + let mut _app = HelloTriangleApplication::initialize(); + // app.main_loop(); +} diff --git a/src/bin/14_command_buffers.rs.diff b/src/bin/14_command_buffers.rs.diff new file mode 100644 index 0000000..021a5ef --- /dev/null +++ b/src/bin/14_command_buffers.rs.diff @@ -0,0 +1,81 @@ +--- a/13_framebuffers.rs ++++ b/14_command_buffers.rs +@@ -34,6 +34,7 @@ use vulkano::sync::SharingMode; + use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, ++ vertex::BufferlessVertices, + viewport::Viewport, + }; + use vulkano::framebuffer::{ +@@ -43,6 +44,11 @@ use vulkano::framebuffer::{ + Framebuffer, + }; + use vulkano::descriptor::PipelineLayoutAbstract; ++use vulkano::command_buffer::{ ++ AutoCommandBuffer, ++ AutoCommandBufferBuilder, ++ DynamicState, ++}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -105,6 +111,8 @@ struct HelloTriangleApplication { + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, ++ ++ command_buffers: Vec>, + } + + impl HelloTriangleApplication { +@@ -125,7 +133,7 @@ impl HelloTriangleApplication { + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + +- Self { ++ let mut app = Self { + instance, + debug_callback, + +@@ -145,7 +153,12 @@ impl HelloTriangleApplication { + graphics_pipeline, + + swap_chain_framebuffers, +- } ++ ++ command_buffers: vec![], ++ }; ++ ++ app.create_command_buffers(); ++ app + } + + fn create_instance() -> Arc { +@@ -405,6 +418,26 @@ impl HelloTriangleApplication { + ).collect::>() + } + ++ fn create_command_buffers(&mut self) { ++ let queue_family = self.graphics_queue.family(); ++ self.command_buffers = self.swap_chain_framebuffers.iter() ++ .map(|framebuffer| { ++ let vertices = BufferlessVertices { vertices: 3, instances: 1 }; ++ Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) ++ .unwrap() ++ .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) ++ .unwrap() ++ .draw(self.graphics_pipeline.clone(), &DynamicState::none(), ++ vertices, (), ()) ++ .unwrap() ++ .end_render_pass() ++ .unwrap() ++ .build() ++ .unwrap()) ++ }) ++ .collect(); ++ } ++ + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? diff --git a/src/bin/15_hello_triangle.rs b/src/bin/15_hello_triangle.rs new file mode 100644 index 0000000..fdd4e34 --- /dev/null +++ b/src/bin/15_hello_triangle.rs @@ -0,0 +1,539 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + vertex::BufferlessVertices, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::descriptor::PipelineLayoutAbstract; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +#[allow(unused)] +struct HelloTriangleApplication { + instance: Arc, + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + // NOTE: We need to the full type of + // self.graphics_pipeline, because `BufferlessVertices` only + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + command_buffers: Vec>, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + command_buffers: vec![], + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + None, + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + let vertices = BufferlessVertices { vertices: 3, instances: 1 }; + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), + vertices, (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + let (image_index, acquire_future) = acquire_next_image(self.swap_chain.clone(), None).unwrap(); + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = acquire_future + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush() + .unwrap(); + + future.wait(None).unwrap(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/15_hello_triangle.rs.diff b/src/bin/15_hello_triangle.rs.diff new file mode 100644 index 0000000..1c8d321 --- /dev/null +++ b/src/bin/15_hello_triangle.rs.diff @@ -0,0 +1,51 @@ +--- a/14_command_buffers.rs ++++ b/15_hello_triangle.rs +@@ -27,10 +27,11 @@ use vulkano::swapchain::{ + PresentMode, + Swapchain, + CompositeAlpha, ++ acquire_next_image, + }; + use vulkano::format::Format; + use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +-use vulkano::sync::SharingMode; ++use vulkano::sync::{SharingMode, GpuFuture}; + use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, +@@ -502,6 +503,8 @@ impl HelloTriangleApplication { + #[allow(unused)] + fn main_loop(&mut self) { + loop { ++ self.draw_frame(); ++ + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { +@@ -513,9 +516,24 @@ impl HelloTriangleApplication { + } + } + } ++ ++ fn draw_frame(&mut self) { ++ let (image_index, acquire_future) = acquire_next_image(self.swap_chain.clone(), None).unwrap(); ++ ++ let command_buffer = self.command_buffers[image_index].clone(); ++ ++ let future = acquire_future ++ .then_execute(self.graphics_queue.clone(), command_buffer) ++ .unwrap() ++ .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) ++ .then_signal_fence_and_flush() ++ .unwrap(); ++ ++ future.wait(None).unwrap(); ++ } + } + + fn main() { +- let mut _app = HelloTriangleApplication::initialize(); +- // app.main_loop(); ++ let mut app = HelloTriangleApplication::initialize(); ++ app.main_loop(); + } diff --git a/src/bin/16_swap_chain_recreation.rs b/src/bin/16_swap_chain_recreation.rs new file mode 100644 index 0000000..bab788b --- /dev/null +++ b/src/bin/16_swap_chain_recreation.rs @@ -0,0 +1,594 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, + vertex::BufferlessVertices, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::descriptor::PipelineLayoutAbstract; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + // NOTE: We need to the full type of + // self.graphics_pipeline, because `BufferlessVertices` only + // works when the concrete type of the graphics pipeline is visible + // to the command buffer. + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/09_shader_base.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/09_shader_base.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input(BufferlessDefinition {}) + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + let vertices = BufferlessVertices { vertices: 3, instances: 1 }; + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), + vertices, (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/16_swap_chain_recreation.rs.diff b/src/bin/16_swap_chain_recreation.rs.diff new file mode 100644 index 0000000..85e6b36 --- /dev/null +++ b/src/bin/16_swap_chain_recreation.rs.diff @@ -0,0 +1,156 @@ +--- a/15_hello_triangle.rs ++++ b/16_swap_chain_recreation.rs +@@ -28,10 +28,11 @@ use vulkano::swapchain::{ + Swapchain, + CompositeAlpha, + acquire_next_image, ++ AcquireError, + }; + use vulkano::format::Format; + use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +-use vulkano::sync::{SharingMode, GpuFuture}; ++use vulkano::sync::{self, SharingMode, GpuFuture}; + use vulkano::pipeline::{ + GraphicsPipeline, + vertex::BufferlessDefinition, +@@ -87,9 +88,9 @@ impl QueueFamilyIndices { + + type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; + +-#[allow(unused)] + struct HelloTriangleApplication { + instance: Arc, ++ #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, +@@ -114,6 +115,9 @@ struct HelloTriangleApplication { + swap_chain_framebuffers: Vec>, + + command_buffers: Vec>, ++ ++ previous_frame_end: Option>, ++ recreate_swap_chain: bool, + } + + impl HelloTriangleApplication { +@@ -127,13 +131,15 @@ impl HelloTriangleApplication { + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, +- &device, &graphics_queue, &present_queue); ++ &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + ++ let previous_frame_end = Some(Self::create_sync_objects(&device)); ++ + let mut app = Self { + instance, + debug_callback, +@@ -156,6 +162,9 @@ impl HelloTriangleApplication { + swap_chain_framebuffers, + + command_buffers: vec![], ++ ++ previous_frame_end, ++ recreate_swap_chain: false, + }; + + app.create_command_buffers(); +@@ -290,6 +299,7 @@ impl HelloTriangleApplication { + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, ++ old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) +@@ -330,7 +340,7 @@ impl HelloTriangleApplication { + CompositeAlpha::Opaque, + present_mode, + true, // clipped +- None, ++ old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) +@@ -439,6 +449,10 @@ impl HelloTriangleApplication { + .collect(); + } + ++ fn create_sync_objects(device: &Arc) -> Box { ++ Box::new(sync::now(device.clone())) as Box ++ } ++ + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? +@@ -518,18 +532,59 @@ impl HelloTriangleApplication { + } + + fn draw_frame(&mut self) { +- let (image_index, acquire_future) = acquire_next_image(self.swap_chain.clone(), None).unwrap(); ++ self.previous_frame_end.as_mut().unwrap().cleanup_finished(); ++ ++ if self.recreate_swap_chain { ++ self.recreate_swap_chain(); ++ self.recreate_swap_chain = false; ++ } ++ ++ let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { ++ Ok(r) => r, ++ Err(AcquireError::OutOfDate) => { ++ self.recreate_swap_chain = true; ++ return; ++ }, ++ Err(err) => panic!("{:?}", err) ++ }; + + let command_buffer = self.command_buffers[image_index].clone(); + +- let future = acquire_future ++ let future = self.previous_frame_end.take().unwrap() ++ .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) +- .then_signal_fence_and_flush() +- .unwrap(); ++ .then_signal_fence_and_flush(); ++ ++ match future { ++ Ok(future) => { ++ self.previous_frame_end = Some(Box::new(future) as Box<_>); ++ } ++ Err(vulkano::sync::FlushError::OutOfDate) => { ++ self.recreate_swap_chain = true; ++ self.previous_frame_end ++ = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); ++ } ++ Err(e) => { ++ println!("{:?}", e); ++ self.previous_frame_end ++ = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); ++ } ++ } ++ } + +- future.wait(None).unwrap(); ++ fn recreate_swap_chain(&mut self) { ++ let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, ++ &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); ++ self.swap_chain = swap_chain; ++ self.swap_chain_images = images; ++ ++ self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); ++ self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), ++ &self.render_pass); ++ self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); ++ self.create_command_buffers(); + } + } + diff --git a/src/bin/17_shader_vertexbuffer.frag b/src/bin/17_shader_vertexbuffer.frag new file mode 100644 index 0000000..84daf5e --- /dev/null +++ b/src/bin/17_shader_vertexbuffer.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/src/bin/17_shader_vertexbuffer.vert b/src/bin/17_shader_vertexbuffer.vert new file mode 100644 index 0000000..3b48be4 --- /dev/null +++ b/src/bin/17_shader_vertexbuffer.vert @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +// NOTE: names must match the `Vertex` struct in Rust +layout(location = 0) in vec2 pos; +layout(location = 1) in vec3 color; + +layout(location = 0) out vec3 fragColor; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = vec4(pos, 0.0, 1.0); + fragColor = color; +} diff --git a/src/bin/17_shader_vertexbuffer.vert.diff b/src/bin/17_shader_vertexbuffer.vert.diff new file mode 100644 index 0000000..c0150dc --- /dev/null +++ b/src/bin/17_shader_vertexbuffer.vert.diff @@ -0,0 +1,36 @@ +--- a/09_shader_base.vert ++++ b/17_shader_vertexbuffer.vert +@@ -1,25 +1,17 @@ + #version 450 + #extension GL_ARB_separate_shader_objects : enable + +-out gl_PerVertex { +- vec4 gl_Position; +-}; ++// NOTE: names must match the `Vertex` struct in Rust ++layout(location = 0) in vec2 pos; ++layout(location = 1) in vec3 color; + + layout(location = 0) out vec3 fragColor; + +-vec2 positions[3] = vec2[]( +- vec2(0.0, -0.5), +- vec2(0.5, 0.5), +- vec2(-0.5, 0.5) +-); +- +-vec3 colors[3] = vec3[]( +- vec3(1.0, 0.0, 0.0), +- vec3(0.0, 1.0, 0.0), +- vec3(0.0, 0.0, 1.0) +-); ++out gl_PerVertex { ++ vec4 gl_Position; ++}; + + void main() { +- gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); +- fragColor = colors[gl_VertexIndex]; ++ gl_Position = vec4(pos, 0.0, 1.0); ++ fragColor = color; + } diff --git a/src/bin/18_vertex_buffer.rs b/src/bin/18_vertex_buffer.rs new file mode 100644 index 0000000..a73cd7c --- /dev/null +++ b/src/bin/18_vertex_buffer.rs @@ -0,0 +1,619 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + cpu_access::CpuAccessibleBuffer, + BufferUsage, + BufferAccess, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} +impl_vertex!(Vertex, pos, color); + +fn vertices() -> [Vertex; 3] { + [ + Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), + Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), + Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) + ] +} + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let vertex_buffer = Self::create_vertex_buffer(&device); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + vertex_buffer, + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/17_shader_vertexbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/17_shader_vertexbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(device: &Arc) -> Arc { + CpuAccessibleBuffer::from_iter(device.clone(), + BufferUsage::vertex_buffer(), vertices().iter().cloned()).unwrap() + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), + vec![self.vertex_buffer.clone()], (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/18_vertex_buffer.rs.diff b/src/bin/18_vertex_buffer.rs.diff new file mode 100644 index 0000000..97d8193 --- /dev/null +++ b/src/bin/18_vertex_buffer.rs.diff @@ -0,0 +1,146 @@ +--- a/16_swap_chain_recreation.rs ++++ b/18_vertex_buffer.rs +@@ -35,8 +35,7 @@ use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; + use vulkano::sync::{self, SharingMode, GpuFuture}; + use vulkano::pipeline::{ + GraphicsPipeline, +- vertex::BufferlessDefinition, +- vertex::BufferlessVertices, ++ GraphicsPipelineAbstract, + viewport::Viewport, + }; + use vulkano::framebuffer::{ +@@ -45,12 +44,16 @@ use vulkano::framebuffer::{ + FramebufferAbstract, + Framebuffer, + }; +-use vulkano::descriptor::PipelineLayoutAbstract; + use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, + }; ++use vulkano::buffer::{ ++ cpu_access::CpuAccessibleBuffer, ++ BufferUsage, ++ BufferAccess, ++}; + + const WIDTH: u32 = 800; + const HEIGHT: u32 = 600; +@@ -86,7 +89,25 @@ impl QueueFamilyIndices { + } + } + +-type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; ++#[derive(Copy, Clone)] ++struct Vertex { ++ pos: [f32; 2], ++ color: [f32; 3], ++} ++impl Vertex { ++ fn new(pos: [f32; 2], color: [f32; 3]) -> Self { ++ Self { pos, color } ++ } ++} ++impl_vertex!(Vertex, pos, color); ++ ++fn vertices() -> [Vertex; 3] { ++ [ ++ Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), ++ Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), ++ Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) ++ ] ++} + + struct HelloTriangleApplication { + instance: Arc, +@@ -106,14 +127,11 @@ struct HelloTriangleApplication { + swap_chain_images: Vec>>, + + render_pass: Arc, +- // NOTE: We need to the full type of +- // self.graphics_pipeline, because `BufferlessVertices` only +- // works when the concrete type of the graphics pipeline is visible +- // to the command buffer. +- graphics_pipeline: Arc, ++ graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + ++ vertex_buffer: Arc, + command_buffers: Vec>, + + previous_frame_end: Option>, +@@ -138,6 +156,8 @@ impl HelloTriangleApplication { + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + ++ let vertex_buffer = Self::create_vertex_buffer(&device); ++ + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { +@@ -161,6 +181,7 @@ impl HelloTriangleApplication { + + swap_chain_framebuffers, + ++ vertex_buffer, + command_buffers: vec![], + + previous_frame_end, +@@ -367,18 +388,18 @@ impl HelloTriangleApplication { + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, +- ) -> Arc { ++ ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", +- path: "src/bin/09_shader_base.vert" ++ path: "src/bin/17_shader_vertexbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", +- path: "src/bin/09_shader_base.frag" ++ path: "src/bin/17_shader_vertexbuffer.frag" + } + } + +@@ -395,7 +416,7 @@ impl HelloTriangleApplication { + }; + + Arc::new(GraphicsPipeline::start() +- .vertex_input(BufferlessDefinition {}) ++ .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) +@@ -429,17 +450,21 @@ impl HelloTriangleApplication { + ).collect::>() + } + ++ fn create_vertex_buffer(device: &Arc) -> Arc { ++ CpuAccessibleBuffer::from_iter(device.clone(), ++ BufferUsage::vertex_buffer(), vertices().iter().cloned()).unwrap() ++ } ++ + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { +- let vertices = BufferlessVertices { vertices: 3, instances: 1 }; + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), +- vertices, (), ()) ++ vec![self.vertex_buffer.clone()], (), ()) + .unwrap() + .end_render_pass() + .unwrap() diff --git a/src/bin/19_staging_buffer.rs b/src/bin/19_staging_buffer.rs new file mode 100644 index 0000000..5c93ffc --- /dev/null +++ b/src/bin/19_staging_buffer.rs @@ -0,0 +1,623 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} +impl_vertex!(Vertex, pos, color); + +fn vertices() -> [Vertex; 3] { + [ + Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), + Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), + Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) + ] +} + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + vertex_buffer, + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/17_shader_vertexbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/17_shader_vertexbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { + let (buffer, future) = ImmutableBuffer::from_iter( + vertices().iter().cloned(), BufferUsage::vertex_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), + vec![self.vertex_buffer.clone()], (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/19_staging_buffer.rs.diff b/src/bin/19_staging_buffer.rs.diff new file mode 100644 index 0000000..85fe09e --- /dev/null +++ b/src/bin/19_staging_buffer.rs.diff @@ -0,0 +1,37 @@ +--- a/18_vertex_buffer.rs ++++ b/19_staging_buffer.rs +@@ -50,7 +50,7 @@ use vulkano::command_buffer::{ + DynamicState, + }; + use vulkano::buffer::{ +- cpu_access::CpuAccessibleBuffer, ++ immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, + }; +@@ -156,7 +156,7 @@ impl HelloTriangleApplication { + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + +- let vertex_buffer = Self::create_vertex_buffer(&device); ++ let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + +@@ -450,9 +450,13 @@ impl HelloTriangleApplication { + ).collect::>() + } + +- fn create_vertex_buffer(device: &Arc) -> Arc { +- CpuAccessibleBuffer::from_iter(device.clone(), +- BufferUsage::vertex_buffer(), vertices().iter().cloned()).unwrap() ++ fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { ++ let (buffer, future) = ImmutableBuffer::from_iter( ++ vertices().iter().cloned(), BufferUsage::vertex_buffer(), ++ graphics_queue.clone()) ++ .unwrap(); ++ future.flush().unwrap(); ++ buffer + } + + fn create_command_buffers(&mut self) { diff --git a/src/bin/20_index_buffer.rs b/src/bin/20_index_buffer.rs new file mode 100644 index 0000000..715a636 --- /dev/null +++ b/src/bin/20_index_buffer.rs @@ -0,0 +1,642 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; + +use std::sync::Arc; +use std::collections::HashSet; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, + TypedBufferAccess, +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} +impl_vertex!(Vertex, pos, color); + +fn vertices() -> [Vertex; 4] { + [ + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), + Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), + Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), + Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) + ] +} + +fn indices() -> [u16; 6] { + [0, 1, 2, 2, 3, 0] +} + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + let index_buffer = Self::create_index_buffer(&graphics_queue); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + vertex_buffer, + index_buffer, + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/17_shader_vertexbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/17_shader_vertexbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { + let (buffer, future) = ImmutableBuffer::from_iter( + vertices().iter().cloned(), BufferUsage::vertex_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { + let (buffer, future) = ImmutableBuffer::from_iter( + indices().iter().cloned(), BufferUsage::index_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), + vec![self.vertex_buffer.clone()], + self.index_buffer.clone(), (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/20_index_buffer.rs.diff b/src/bin/20_index_buffer.rs.diff new file mode 100644 index 0000000..808ad3b --- /dev/null +++ b/src/bin/20_index_buffer.rs.diff @@ -0,0 +1,86 @@ +--- a/19_staging_buffer.rs ++++ b/20_index_buffer.rs +@@ -53,6 +53,7 @@ use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, ++ TypedBufferAccess, + }; + + const WIDTH: u32 = 800; +@@ -101,14 +102,19 @@ impl Vertex { + } + impl_vertex!(Vertex, pos, color); + +-fn vertices() -> [Vertex; 3] { ++fn vertices() -> [Vertex; 4] { + [ +- Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), +- Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), +- Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) ++ Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), ++ Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), ++ Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), ++ Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) + ] + } + ++fn indices() -> [u16; 6] { ++ [0, 1, 2, 2, 3, 0] ++} ++ + struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] +@@ -132,6 +138,7 @@ struct HelloTriangleApplication { + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, ++ index_buffer: Arc + Send + Sync>, + command_buffers: Vec>, + + previous_frame_end: Option>, +@@ -157,6 +164,7 @@ impl HelloTriangleApplication { + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); ++ let index_buffer = Self::create_index_buffer(&graphics_queue); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + +@@ -182,6 +190,7 @@ impl HelloTriangleApplication { + swap_chain_framebuffers, + + vertex_buffer, ++ index_buffer, + command_buffers: vec![], + + previous_frame_end, +@@ -459,6 +468,15 @@ impl HelloTriangleApplication { + buffer + } + ++ fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { ++ let (buffer, future) = ImmutableBuffer::from_iter( ++ indices().iter().cloned(), BufferUsage::index_buffer(), ++ graphics_queue.clone()) ++ .unwrap(); ++ future.flush().unwrap(); ++ buffer ++ } ++ + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() +@@ -467,8 +485,9 @@ impl HelloTriangleApplication { + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() +- .draw(self.graphics_pipeline.clone(), &DynamicState::none(), +- vec![self.vertex_buffer.clone()], (), ()) ++ .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), ++ vec![self.vertex_buffer.clone()], ++ self.index_buffer.clone(), (), ()) + .unwrap() + .end_render_pass() + .unwrap() diff --git a/src/bin/21_descriptor_layout_and_buffer.rs b/src/bin/21_descriptor_layout_and_buffer.rs new file mode 100644 index 0000000..960d742 --- /dev/null +++ b/src/bin/21_descriptor_layout_and_buffer.rs @@ -0,0 +1,730 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; +extern crate cgmath; + +use std::sync::Arc; +use std::collections::HashSet; +use std::time::Instant; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, + TypedBufferAccess, + CpuAccessibleBuffer, +}; +use cgmath::{ + Rad, + Deg, + Matrix4, + Vector3, + Point3 +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} + +#[allow(clippy:ref_in_deref)] +impl_vertex!(Vertex, pos, color); + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct UniformBufferObject { + model: Matrix4, + view: Matrix4, + proj: Matrix4, +} + +fn vertices() -> [Vertex; 4] { + [ + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), + Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), + Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), + Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) + ] +} + +fn indices() -> [u16; 6] { + [0, 1, 2, 2, 3, 0] +} + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, + + #[allow(dead_code)] + uniform_buffers: Vec>>, + + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, + + #[allow(dead_code)] + start_time: Instant, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let start_time = Instant::now(); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + let index_buffer = Self::create_index_buffer(&graphics_queue); + let uniform_buffers = Self::create_uniform_buffers(&device, swap_chain_images.len(), start_time, swap_chain.dimensions()); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + vertex_buffer, + index_buffer, + uniform_buffers, + + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + + start_time + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/21_shader_uniformbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/21_shader_uniformbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { + let (buffer, future) = ImmutableBuffer::from_iter( + vertices().iter().cloned(), BufferUsage::vertex_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { + let (buffer, future) = ImmutableBuffer::from_iter( + indices().iter().cloned(), BufferUsage::index_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_uniform_buffers( + device: &Arc, + num_buffers: usize, + start_time: Instant, + dimensions_u32: [u32; 2] + ) -> Vec>> { + let mut buffers = Vec::new(); + + let dimensions = [dimensions_u32[0] as f32, dimensions_u32[1] as f32]; + + let uniform_buffer = Self::update_uniform_buffer(start_time, dimensions); + + for _ in 0..num_buffers { + let buffer = CpuAccessibleBuffer::from_data( + device.clone(), + BufferUsage::uniform_buffer_transfer_destination(), + uniform_buffer, + ).unwrap(); + + buffers.push(buffer); + } + + buffers + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw_indexed( + self.graphics_pipeline.clone(), + &DynamicState::none(), + vec![self.vertex_buffer.clone()], + self.index_buffer.clone(), + (), + ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn update_uniform_buffer(start_time: Instant, dimensions: [f32; 2]) -> UniformBufferObject { + let duration = Instant::now().duration_since(start_time); + let elapsed = (duration.as_secs() * 1000) + u64::from(duration.subsec_millis()); + + let model = Matrix4::from_angle_z(Rad::from(Deg(elapsed as f32 * 0.180))); + + let view = Matrix4::look_at( + Point3::new(2.0, 2.0, 2.0), + Point3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 0.0, 1.0) + ); + + let mut proj = cgmath::perspective( + Rad::from(Deg(45.0)), + dimensions[0] as f32 / dimensions[1] as f32, + 0.1, + 10.0 + ); + + proj.y.y *= -1.0; + + UniformBufferObject { model, view, proj } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} \ No newline at end of file diff --git a/src/bin/21_descriptor_layout_and_buffer.rs.diff b/src/bin/21_descriptor_layout_and_buffer.rs.diff new file mode 100644 index 0000000..f8cbca4 --- /dev/null +++ b/src/bin/21_descriptor_layout_and_buffer.rs.diff @@ -0,0 +1,201 @@ +--- a/20_index_buffer.rs ++++ b/21_descriptor_layout_and_buffer.rs +@@ -2,9 +2,11 @@ + extern crate vulkano; + extern crate vulkano_win; + extern crate winit; ++extern crate cgmath; + + use std::sync::Arc; + use std::collections::HashSet; ++use std::time::Instant; + + use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; + use vulkano_win::VkSurfaceBuild; +@@ -54,6 +56,14 @@ use vulkano::buffer::{ + BufferUsage, + BufferAccess, + TypedBufferAccess, ++ CpuAccessibleBuffer, ++}; ++use cgmath::{ ++ Rad, ++ Deg, ++ Matrix4, ++ Vector3, ++ Point3 + }; + + const WIDTH: u32 = 800; +@@ -100,8 +110,18 @@ impl Vertex { + Self { pos, color } + } + } ++ ++#[allow(clippy:ref_in_deref)] + impl_vertex!(Vertex, pos, color); + ++#[allow(dead_code)] ++#[derive(Copy, Clone)] ++struct UniformBufferObject { ++ model: Matrix4, ++ view: Matrix4, ++ proj: Matrix4, ++} ++ + fn vertices() -> [Vertex; 4] { + [ + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), +@@ -139,10 +159,17 @@ struct HelloTriangleApplication { + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, ++ ++ #[allow(dead_code)] ++ uniform_buffers: Vec>>, ++ + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, ++ ++ #[allow(dead_code)] ++ start_time: Instant, + } + + impl HelloTriangleApplication { +@@ -159,12 +186,16 @@ impl HelloTriangleApplication { + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); ++ + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + ++ let start_time = Instant::now(); ++ + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + let index_buffer = Self::create_index_buffer(&graphics_queue); ++ let uniform_buffers = Self::create_uniform_buffers(&device, swap_chain_images.len(), start_time, swap_chain.dimensions()); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + +@@ -191,10 +222,14 @@ impl HelloTriangleApplication { + + vertex_buffer, + index_buffer, ++ uniform_buffers, ++ + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, ++ ++ start_time + }; + + app.create_command_buffers(); +@@ -401,14 +436,14 @@ impl HelloTriangleApplication { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", +- path: "src/bin/17_shader_vertexbuffer.vert" ++ path: "src/bin/21_shader_uniformbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", +- path: "src/bin/17_shader_vertexbuffer.frag" ++ path: "src/bin/21_shader_uniformbuffer.frag" + } + } + +@@ -477,6 +512,31 @@ impl HelloTriangleApplication { + buffer + } + ++ fn create_uniform_buffers( ++ device: &Arc, ++ num_buffers: usize, ++ start_time: Instant, ++ dimensions_u32: [u32; 2] ++ ) -> Vec>> { ++ let mut buffers = Vec::new(); ++ ++ let dimensions = [dimensions_u32[0] as f32, dimensions_u32[1] as f32]; ++ ++ let uniform_buffer = Self::update_uniform_buffer(start_time, dimensions); ++ ++ for _ in 0..num_buffers { ++ let buffer = CpuAccessibleBuffer::from_data( ++ device.clone(), ++ BufferUsage::uniform_buffer_transfer_destination(), ++ uniform_buffer, ++ ).unwrap(); ++ ++ buffers.push(buffer); ++ } ++ ++ buffers ++ } ++ + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() +@@ -485,9 +545,13 @@ impl HelloTriangleApplication { + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() +- .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), ++ .draw_indexed( ++ self.graphics_pipeline.clone(), ++ &DynamicState::none(), + vec![self.vertex_buffer.clone()], +- self.index_buffer.clone(), (), ()) ++ self.index_buffer.clone(), ++ (), ++ ()) + .unwrap() + .end_render_pass() + .unwrap() +@@ -622,6 +686,30 @@ impl HelloTriangleApplication { + } + } + ++ fn update_uniform_buffer(start_time: Instant, dimensions: [f32; 2]) -> UniformBufferObject { ++ let duration = Instant::now().duration_since(start_time); ++ let elapsed = (duration.as_secs() * 1000) + u64::from(duration.subsec_millis()); ++ ++ let model = Matrix4::from_angle_z(Rad::from(Deg(elapsed as f32 * 0.180))); ++ ++ let view = Matrix4::look_at( ++ Point3::new(2.0, 2.0, 2.0), ++ Point3::new(0.0, 0.0, 0.0), ++ Vector3::new(0.0, 0.0, 1.0) ++ ); ++ ++ let mut proj = cgmath::perspective( ++ Rad::from(Deg(45.0)), ++ dimensions[0] as f32 / dimensions[1] as f32, ++ 0.1, ++ 10.0 ++ ); ++ ++ proj.y.y *= -1.0; ++ ++ UniformBufferObject { model, view, proj } ++ } ++ + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); +@@ -639,4 +727,4 @@ impl HelloTriangleApplication { + fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +-} ++} +\ No newline at end of file diff --git a/src/bin/21_shader_uniformbuffer.frag b/src/bin/21_shader_uniformbuffer.frag new file mode 100644 index 0000000..84daf5e --- /dev/null +++ b/src/bin/21_shader_uniformbuffer.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/src/bin/21_shader_uniformbuffer.vert b/src/bin/21_shader_uniformbuffer.vert new file mode 100644 index 0000000..36af825 --- /dev/null +++ b/src/bin/21_shader_uniformbuffer.vert @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) uniform UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +} ubo; + +// NOTE: names must match the `Vertex` struct in Rust +layout(location = 0) in vec2 pos; +layout(location = 1) in vec3 color; + +layout(location = 0) out vec3 fragColor; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(pos, 0.0, 1.0); + fragColor = color; +} \ No newline at end of file diff --git a/src/bin/21_shader_uniformbuffer.vert.diff b/src/bin/21_shader_uniformbuffer.vert.diff new file mode 100644 index 0000000..d6aad1b --- /dev/null +++ b/src/bin/21_shader_uniformbuffer.vert.diff @@ -0,0 +1,25 @@ +--- a/17_shader_vertexbuffer.vert ++++ b/21_shader_uniformbuffer.vert +@@ -1,6 +1,12 @@ + #version 450 + #extension GL_ARB_separate_shader_objects : enable + ++layout(binding = 0) uniform UniformBufferObject { ++ mat4 model; ++ mat4 view; ++ mat4 proj; ++} ubo; ++ + // NOTE: names must match the `Vertex` struct in Rust + layout(location = 0) in vec2 pos; + layout(location = 1) in vec3 color; +@@ -12,6 +18,6 @@ out gl_PerVertex { + }; + + void main() { +- gl_Position = vec4(pos, 0.0, 1.0); ++ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(pos, 0.0, 1.0); + fragColor = color; +-} ++} +\ No newline at end of file diff --git a/src/bin/22_descriptor_pools_and_sets.rs b/src/bin/22_descriptor_pools_and_sets.rs new file mode 100644 index 0000000..117d66a --- /dev/null +++ b/src/bin/22_descriptor_pools_and_sets.rs @@ -0,0 +1,784 @@ +#[macro_use] +extern crate vulkano; +extern crate vulkano_win; +extern crate winit; +extern crate cgmath; + +use std::sync::{Arc, Mutex}; +use std::collections::HashSet; +use std::time::Instant; + +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + +use vulkano::instance::{ + Instance, + InstanceExtensions, + ApplicationInfo, + Version, + layers_list, + PhysicalDevice, +}; +use vulkano::instance::debug::{DebugCallback, MessageTypes}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; +use vulkano::swapchain::{ + Surface, + Capabilities, + ColorSpace, + SupportedPresentModes, + PresentMode, + Swapchain, + CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, + TypedBufferAccess, + CpuAccessibleBuffer +}; +use vulkano::descriptor::descriptor_set::{ + FixedSizeDescriptorSetsPool, + FixedSizeDescriptorSet +}; +use cgmath::{ + Rad, + Deg, + Matrix4, + Vector3, + Point3 +}; + +const WIDTH: u32 = 800; +const HEIGHT: u32 = 600; + +const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_LUNARG_standard_validation" +]; + +/// Required device extensions +fn device_extensions() -> DeviceExtensions { + DeviceExtensions { + khr_swapchain: true, + .. vulkano::device::DeviceExtensions::none() + } +} + +#[cfg(all(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = true; +#[cfg(not(debug_assertions))] +const ENABLE_VALIDATION_LAYERS: bool = false; + +struct QueueFamilyIndices { + graphics_family: i32, + present_family: i32, +} +impl QueueFamilyIndices { + fn new() -> Self { + Self { graphics_family: -1, present_family: -1 } + } + + fn is_complete(&self) -> bool { + self.graphics_family >= 0 && self.present_family >= 0 + } +} + +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} +impl_vertex!(Vertex, pos, color); + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct UniformBufferObject { + model: Matrix4, + view: Matrix4, + proj: Matrix4, +} + +fn vertices() -> [Vertex; 4] { + [ + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), + Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), + Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), + Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) + ] +} + +fn indices() -> [u16; 6] { + [0, 1, 2, 2, 3, 0] +} + +struct HelloTriangleApplication { + instance: Arc, + #[allow(unused)] + debug_callback: Option, + + events_loop: EventsLoop, + surface: Arc>, + + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) + device: Arc, + + graphics_queue: Arc, + present_queue: Arc, + + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, + uniform_buffers: Vec>>, + + descriptor_sets: Vec, ((), vulkano::descriptor::descriptor_set::PersistentDescriptorSetBuf>>)>>>, + + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, + + start_time: Instant, +} + +impl HelloTriangleApplication { + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); + + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); + + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let start_time = Instant::now(); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + let index_buffer = Self::create_index_buffer(&graphics_queue); + let uniform_buffers = Self::create_uniform_buffers(&device, swap_chain_images.len(), start_time, swap_chain.dimensions()); + + let descriptor_sets_pool = Self::create_descriptor_pool(&graphics_pipeline); + let descriptor_sets = Self::create_descriptor_sets(&descriptor_sets_pool, &uniform_buffers); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, + + vertex_buffer, + index_buffer, + uniform_buffers, + + descriptor_sets, + + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + + start_time + }; + + app.create_command_buffers(); + app + } + + fn create_instance() -> Arc { + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { + println!("Validation layers requested, but not available!") + } + + let supported_extensions = InstanceExtensions::supported_by_core() + .expect("failed to retrieve supported extensions"); + println!("Supported extensions: {:?}", supported_extensions); + + let app_info = ApplicationInfo { + application_name: Some("Hello Triangle".into()), + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), + engine_name: Some("No Engine".into()), + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), + }; + + let required_extensions = Self::get_required_extensions(); + + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions + } + + fn setup_debug_callback(instance: &Arc) -> Option { + if !ENABLE_VALIDATION_LAYERS { + return None; + } + + let msg_types = MessageTypes { + error: true, + warning: true, + performance_warning: true, + information: false, + debug: true, + }; + DebugCallback::new(&instance, msg_types, |msg| { + println!("validation layer: {:?}", msg.description); + }).ok() + } + + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") + } + + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); + let extensions_supported = Self::check_device_extension_support(device); + + let swap_chain_adequate = if extensions_supported { + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); + !capabilities.supported_formats.is_empty() && + capabilities.present_modes.iter().next().is_some() + } else { + false + }; + + indices.is_complete() && extensions_supported && swap_chain_adequate + } + + fn check_device_extension_support(device: &PhysicalDevice) -> bool { + let available_extensions = DeviceExtensions::supported_by_device(*device); + let device_extensions = device_extensions(); + available_extensions.intersection(&device_extensions) == device_extensions + } + + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) + *available_formats.iter() + .find(|(format, color_space)| + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear + ) + .unwrap_or_else(|| &available_formats[0]) + } + + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { + if available_present_modes.mailbox { + PresentMode::Mailbox + } else if available_present_modes.immediate { + PresentMode::Immediate + } else { + PresentMode::Fifo + } + } + + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { + if let Some(current_extent) = capabilities.current_extent { + return current_extent + } else { + let mut actual_extent = [WIDTH, HEIGHT]; + actual_extent[0] = capabilities.min_image_extent[0] + .max(capabilities.max_image_extent[0].min(actual_extent[0])); + actual_extent[1] = capabilities.min_image_extent[1] + .max(capabilities.max_image_extent[1].min(actual_extent[1])); + actual_extent + } + } + + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); + + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); + let extent = Self::choose_swap_extent(&capabilities); + + let mut image_count = capabilities.min_image_count + 1; + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { + image_count = capabilities.max_image_count.unwrap(); + } + + let image_usage = ImageUsage { + color_attachment: true, + .. ImageUsage::none() + }; + + let indices = Self::find_queue_families(&surface, &physical_device); + + let sharing: SharingMode = if indices.graphics_family != indices.present_family { + vec![graphics_queue, present_queue].as_slice().into() + } else { + graphics_queue.into() + }; + + let (swap_chain, images) = Swapchain::new( + device.clone(), + surface.clone(), + image_count, + surface_format.0, // TODO: color space? + extent, + 1, // layers + image_usage, + sharing, + capabilities.current_transform, + CompositeAlpha::Opaque, + present_mode, + true, // clipped + old_swapchain.as_ref() + ).expect("failed to create swap chain!"); + + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/21_shader_uniformbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/21_shader_uniformbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_counter_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { + let (buffer, future) = ImmutableBuffer::from_iter( + vertices().iter().cloned(), BufferUsage::vertex_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { + let (buffer, future) = ImmutableBuffer::from_iter( + indices().iter().cloned(), BufferUsage::index_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_uniform_buffers( + device: &Arc, + num_buffers: usize, + start_time: Instant, + dimensions_u32: [u32; 2] + ) -> Vec>> { + let mut buffers = Vec::new(); + + let dimensions = [dimensions_u32[0] as f32, dimensions_u32[1] as f32]; + + let uniform_buffer = Self::update_uniform_buffer(start_time, dimensions); + + for _ in 0..num_buffers { + let buffer = CpuAccessibleBuffer::from_data( + device.clone(), + BufferUsage::uniform_buffer_transfer_destination(), + uniform_buffer, + ).unwrap(); + + buffers.push(buffer); + } + + buffers + } + + fn create_descriptor_pool(graphics_pipeline: &Arc) + -> Arc>>> + { + Arc::new( + Mutex::new( + FixedSizeDescriptorSetsPool::new(graphics_pipeline.clone(), 0) + ) + ) + } + + fn create_descriptor_sets( + pool: &Arc>>>, + uniform_buffers: &[Arc>], + ) -> Vec, ((), vulkano::descriptor::descriptor_set::PersistentDescriptorSetBuf>>)>>> + { + uniform_buffers + .iter() + .map(|uniform_buffer| + Arc::new( + pool + .lock() + .unwrap() + .next() + .add_buffer(uniform_buffer.clone()) + .unwrap() + .build() + .unwrap() + ) + ) + .collect() + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + let dimensions = [self.swap_chain.dimensions()[0] as f32, self.swap_chain.dimensions()[1] as f32]; + + self.command_buffers = self.swap_chain_framebuffers + .iter() + .enumerate() + .map(|(i, framebuffer)| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .update_buffer(self.uniform_buffers[i].clone(), Self::update_uniform_buffer(self.start_time, dimensions)) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw_indexed( + self.graphics_pipeline.clone(), + &DynamicState::none(), + vec![self.vertex_buffer.clone()], + self.index_buffer.clone(), + self.descriptor_sets[i].clone(), + ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box + } + + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { + let mut indices = QueueFamilyIndices::new(); + // TODO: replace index with id to simplify? + for (i, queue_family) in device.queue_families().enumerate() { + if queue_family.supports_graphics() { + indices.graphics_family = i as i32; + } + + if surface.is_supported(queue_family).unwrap() { + indices.present_family = i as i32; + } + + if indices.is_complete() { + break; + } + } + + indices + } + + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.create_command_buffers(); + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true + } + }); + if done { + return; + } + } + } + + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; + } + + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); + + // we're joining on the previous future but the CPU is running faster than the GPU so + // eventually it stutters, and jumps ahead to the newer frames. + // + // See vulkano issue 1135: https://github.com/vulkano-rs/vulkano/issues/1135 + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + + match future { + Ok(future) => { + // This makes sure the CPU stays in sync with the GPU in situations when the CPU is + // running "too fast" + #[cfg(target_os = "macos")] + future.wait(None).unwrap(); + + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } + } + + fn update_uniform_buffer(start_time: Instant, dimensions: [f32; 2]) -> UniformBufferObject { + let duration = Instant::now().duration_since(start_time); + let elapsed = (duration.as_secs() * 1000) + u64::from(duration.subsec_millis()); + + let model = Matrix4::from_angle_z(Rad::from(Deg(elapsed as f32 * 0.180))); + + let view = Matrix4::look_at( + Point3::new(2.0, 2.0, 2.0), + Point3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 0.0, 1.0) + ); + + let mut proj = cgmath::perspective( + Rad::from(Deg(45.0)), + dimensions[0] as f32 / dimensions[1] as f32, + 0.1, + 10.0 + ); + + proj.y.y *= -1.0; + + UniformBufferObject { model, view, proj } + } + + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); + } +} + +fn main() { + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); +} diff --git a/src/bin/22_descriptor_pools_and_sets.rs.diff b/src/bin/22_descriptor_pools_and_sets.rs.diff new file mode 100644 index 0000000..94fb496 --- /dev/null +++ b/src/bin/22_descriptor_pools_and_sets.rs.diff @@ -0,0 +1,150 @@ +--- a/21_descriptor_layout_and_buffer.rs ++++ b/22_descriptor_pools_and_sets.rs +@@ -4,7 +4,7 @@ extern crate vulkano_win; + extern crate winit; + extern crate cgmath; + +-use std::sync::Arc; ++use std::sync::{Arc, Mutex}; + use std::collections::HashSet; + use std::time::Instant; + +@@ -56,7 +56,11 @@ use vulkano::buffer::{ + BufferUsage, + BufferAccess, + TypedBufferAccess, +- CpuAccessibleBuffer, ++ CpuAccessibleBuffer ++}; ++use vulkano::descriptor::descriptor_set::{ ++ FixedSizeDescriptorSetsPool, ++ FixedSizeDescriptorSet + }; + use cgmath::{ + Rad, +@@ -110,8 +114,6 @@ impl Vertex { + Self { pos, color } + } + } +- +-#[allow(clippy:ref_in_deref)] + impl_vertex!(Vertex, pos, color); + + #[allow(dead_code)] +@@ -159,16 +161,15 @@ struct HelloTriangleApplication { + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, +- +- #[allow(dead_code)] + uniform_buffers: Vec>>, + ++ descriptor_sets: Vec, ((), vulkano::descriptor::descriptor_set::PersistentDescriptorSetBuf>>)>>>, ++ + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, + +- #[allow(dead_code)] + start_time: Instant, + } + +@@ -197,6 +198,9 @@ impl HelloTriangleApplication { + let index_buffer = Self::create_index_buffer(&graphics_queue); + let uniform_buffers = Self::create_uniform_buffers(&device, swap_chain_images.len(), start_time, swap_chain.dimensions()); + ++ let descriptor_sets_pool = Self::create_descriptor_pool(&graphics_pipeline); ++ let descriptor_sets = Self::create_descriptor_sets(&descriptor_sets_pool, &uniform_buffers); ++ + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { +@@ -224,6 +228,8 @@ impl HelloTriangleApplication { + index_buffer, + uniform_buffers, + ++ descriptor_sets, ++ + command_buffers: vec![], + + previous_frame_end, +@@ -471,7 +477,7 @@ impl HelloTriangleApplication { + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() +- .front_face_clockwise() ++ .front_face_counter_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) +@@ -537,12 +543,50 @@ impl HelloTriangleApplication { + buffers + } + ++ fn create_descriptor_pool(graphics_pipeline: &Arc) ++ -> Arc>>> ++ { ++ Arc::new( ++ Mutex::new( ++ FixedSizeDescriptorSetsPool::new(graphics_pipeline.clone(), 0) ++ ) ++ ) ++ } ++ ++ fn create_descriptor_sets( ++ pool: &Arc>>>, ++ uniform_buffers: &[Arc>], ++ ) -> Vec, ((), vulkano::descriptor::descriptor_set::PersistentDescriptorSetBuf>>)>>> ++ { ++ uniform_buffers ++ .iter() ++ .map(|uniform_buffer| ++ Arc::new( ++ pool ++ .lock() ++ .unwrap() ++ .next() ++ .add_buffer(uniform_buffer.clone()) ++ .unwrap() ++ .build() ++ .unwrap() ++ ) ++ ) ++ .collect() ++ } ++ + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); +- self.command_buffers = self.swap_chain_framebuffers.iter() +- .map(|framebuffer| { ++ let dimensions = [self.swap_chain.dimensions()[0] as f32, self.swap_chain.dimensions()[1] as f32]; ++ ++ self.command_buffers = self.swap_chain_framebuffers ++ .iter() ++ .enumerate() ++ .map(|(i, framebuffer)| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() ++ .update_buffer(self.uniform_buffers[i].clone(), Self::update_uniform_buffer(self.start_time, dimensions)) ++ .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw_indexed( +@@ -550,7 +594,7 @@ impl HelloTriangleApplication { + &DynamicState::none(), + vec![self.vertex_buffer.clone()], + self.index_buffer.clone(), +- (), ++ self.descriptor_sets[i].clone(), + ()) + .unwrap() + .end_render_pass() +@@ -629,6 +673,7 @@ impl HelloTriangleApplication { + #[allow(unused)] + fn main_loop(&mut self) { + loop { ++ self.create_command_buffers(); + self.draw_frame(); + + let mut done = false; diff --git a/src/bin/diff.sh b/src/bin/diff.sh new file mode 100755 index 0000000..b40d668 --- /dev/null +++ b/src/bin/diff.sh @@ -0,0 +1,3 @@ +#!/bin/bash +git diff --color-words --no-index "$1" "$2" +git diff --no-index "$1" "$2" | tail -n+3 > "$2".diff diff --git a/src/main.rs b/src/main.rs index b5fbf62..715a636 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,60 @@ +#[macro_use] extern crate vulkano; -extern crate winit; extern crate vulkano_win; +extern crate winit; use std::sync::Arc; use std::collections::HashSet; +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; +use vulkano_win::VkSurfaceBuild; + use vulkano::instance::{ Instance, InstanceExtensions, - layers_list, ApplicationInfo, Version, + layers_list, PhysicalDevice, - Features, }; use vulkano::instance::debug::{DebugCallback, MessageTypes}; -use vulkano::device::{Device, DeviceExtensions, Queue}; +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; use vulkano::swapchain::{ Surface, Capabilities, ColorSpace, - SupportedPresentModes, PresentMode, + SupportedPresentModes, + PresentMode, Swapchain, CompositeAlpha, + acquire_next_image, + AcquireError, +}; +use vulkano::format::Format; +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; +use vulkano::sync::{self, SharingMode, GpuFuture}; +use vulkano::pipeline::{ + GraphicsPipeline, + GraphicsPipelineAbstract, + viewport::Viewport, +}; +use vulkano::framebuffer::{ + RenderPassAbstract, + Subpass, + FramebufferAbstract, + Framebuffer, +}; +use vulkano::command_buffer::{ + AutoCommandBuffer, + AutoCommandBufferBuilder, + DynamicState, +}; +use vulkano::buffer::{ + immutable::ImmutableBuffer, + BufferUsage, + BufferAccess, + TypedBufferAccess, }; -use vulkano::format::{Format}; -use vulkano::image::ImageUsage; -use vulkano::image::swapchain::SwapchainImage; -use vulkano::sync::SharingMode; - -use winit::WindowBuilder; -use winit::dpi::LogicalSize; -use vulkano_win::VkSurfaceBuild; const WIDTH: u32 = 800; const HEIGHT: u32 = 600; @@ -48,10 +71,9 @@ fn device_extensions() -> DeviceExtensions { } } -// MoltenVK doesn't have any layers by default -#[cfg(all(debug_assertions, not(target_os = "macos")))] +#[cfg(all(debug_assertions))] const ENABLE_VALIDATION_LAYERS: bool = true; -#[cfg(any(not(debug_assertions), target_os = "macos"))] +#[cfg(not(debug_assertions))] const ENABLE_VALIDATION_LAYERS: bool = false; struct QueueFamilyIndices { @@ -68,59 +90,125 @@ impl QueueFamilyIndices { } } -#[derive(Default)] +#[derive(Copy, Clone)] +struct Vertex { + pos: [f32; 2], + color: [f32; 3], +} +impl Vertex { + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { + Self { pos, color } + } +} +impl_vertex!(Vertex, pos, color); + +fn vertices() -> [Vertex; 4] { + [ + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), + Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), + Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), + Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) + ] +} + +fn indices() -> [u16; 6] { + [0, 1, 2, 2, 3, 0] +} + struct HelloTriangleApplication { - instance: Option>, + instance: Arc, + #[allow(unused)] debug_callback: Option, - surface: Option>>, + + events_loop: EventsLoop, + surface: Arc>, physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) - device: Option>, + device: Arc, - graphics_queue: Option>, - present_queue: Option>, + graphics_queue: Arc, + present_queue: Arc, - swap_chain: Option>>, - swap_chain_images: Option>>>, - swap_chain_image_format: Option, - swap_chain_extent: Option<[u32; 2]>, + swap_chain: Arc>, + swap_chain_images: Vec>>, + + render_pass: Arc, + graphics_pipeline: Arc, + + swap_chain_framebuffers: Vec>, + + vertex_buffer: Arc, + index_buffer: Arc + Send + Sync>, + command_buffers: Vec>, + + previous_frame_end: Option>, + recreate_swap_chain: bool, } -#[allow(dead_code)] // TODO: TMP + impl HelloTriangleApplication { - pub fn new() -> Self { - Default::default() - } + pub fn initialize() -> Self { + let instance = Self::create_instance(); + let debug_callback = Self::setup_debug_callback(&instance); + let (events_loop, surface) = Self::create_surface(&instance); - pub fn run(&mut self) { - self.init_window(); - self.init_vulkan(); - self.main_loop(); - self.cleanup(); - } + let physical_device_index = Self::pick_physical_device(&instance, &surface); + let (device, graphics_queue, present_queue) = Self::create_logical_device( + &instance, &surface, physical_device_index); - fn init_window(&self) { - WindowBuilder::new() - .with_title("Vulkan") - .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); - } + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, + &device, &graphics_queue, &present_queue, None); + + let render_pass = Self::create_render_pass(&device, swap_chain.format()); + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); + + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); + + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); + let index_buffer = Self::create_index_buffer(&graphics_queue); + + let previous_frame_end = Some(Self::create_sync_objects(&device)); + + let mut app = Self { + instance, + debug_callback, + + events_loop, + surface, + + physical_device_index, + device, + + graphics_queue, + present_queue, + + swap_chain, + swap_chain_images, + + render_pass, + graphics_pipeline, + + swap_chain_framebuffers, - fn init_vulkan(&mut self) { - self.create_instance(); - self.setup_debug_callback(); - self.create_surface(); - self.pick_physical_device(); - self.create_logical_device(); - self.create_swap_chain(); + vertex_buffer, + index_buffer, + command_buffers: vec![], + + previous_frame_end, + recreate_swap_chain: false, + }; + + app.create_command_buffers(); + app } - fn create_instance(&mut self) { + fn create_instance() -> Arc { if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { - panic!("validation layers requested, but not available!") + println!("Validation layers requested, but not available!") } - let extensions = InstanceExtensions::supported_by_core() + let supported_extensions = InstanceExtensions::supported_by_core() .expect("failed to retrieve supported extensions"); - println!("Supported extensions: {:?}", extensions); + println!("Supported extensions: {:?}", supported_extensions); let app_info = ApplicationInfo { application_name: Some("Hello Triangle".into()), @@ -129,25 +217,38 @@ impl HelloTriangleApplication { engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), }; - let extensions = Self::get_required_extensions(); + let required_extensions = Self::get_required_extensions(); - let instance = - if ENABLE_VALIDATION_LAYERS { - Instance::new(Some(&app_info), &extensions, VALIDATION_LAYERS.iter().map(|s| *s)) - .expect("failed to create Vulkan instance") - } else { - Instance::new(Some(&app_info), &extensions, None) - .expect("failed to create Vulkan instance") - }; - self.instance = Some(instance); + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) + .expect("failed to create Vulkan instance") + } else { + Instance::new(Some(&app_info), &required_extensions, None) + .expect("failed to create Vulkan instance") + } + } + + fn check_validation_layer_support() -> bool { + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); + VALIDATION_LAYERS.iter() + .all(|layer_name| layers.contains(&layer_name.to_string())) + } + + fn get_required_extensions() -> InstanceExtensions { + let mut extensions = vulkano_win::required_extensions(); + if ENABLE_VALIDATION_LAYERS { + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano + extensions.ext_debug_report = true; + } + + extensions } - fn setup_debug_callback(&mut self) { + fn setup_debug_callback(instance: &Arc) -> Option { if !ENABLE_VALIDATION_LAYERS { - return; + return None; } - let instance = self.instance.as_ref().unwrap(); let msg_types = MessageTypes { error: true, warning: true, @@ -155,24 +256,24 @@ impl HelloTriangleApplication { information: false, debug: true, }; - self.debug_callback = DebugCallback::new(instance, msg_types, |msg| { + DebugCallback::new(&instance, msg_types, |msg| { println!("validation layer: {:?}", msg.description); - }).ok(); + }).ok() } - fn pick_physical_device(&mut self) { - let instance = self.instance.as_ref().unwrap(); - self.physical_device_index = PhysicalDevice::enumerate(&instance) - .position(|device| self.is_device_suitable(&device)) - .expect("failed to find a suitable GPU!"); + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { + PhysicalDevice::enumerate(&instance) + .position(|device| Self::is_device_suitable(surface, &device)) + .expect("failed to find a suitable GPU!") } - fn is_device_suitable(&self, device: &PhysicalDevice) -> bool { - let indices = Self::find_queue_families(device); + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { + let indices = Self::find_queue_families(surface, device); let extensions_supported = Self::check_device_extension_support(device); let swap_chain_adequate = if extensions_supported { - let capabilities = self.query_swap_chain_support(device); + let capabilities = surface.capabilities(*device) + .expect("failed to get surface capabilities"); !capabilities.supported_formats.is_empty() && capabilities.present_modes.iter().next().is_some() } else { @@ -188,52 +289,6 @@ impl HelloTriangleApplication { available_extensions.intersection(&device_extensions) == device_extensions } - fn create_logical_device(&mut self) { - let instance = self.instance.as_ref().unwrap(); - let physical_device = PhysicalDevice::from_index(instance, self.physical_device_index).unwrap(); - - let indices = Self::find_queue_families(&physical_device); - - let families = [indices.graphics_family, indices.present_family]; - use std::iter::FromIterator; - let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); - - let queue_priority = 1.0; - let queue_families = unique_queue_families.iter().map(|i| { - (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) - }); - - // NOTE: the tutorial recommends passing the validation layers as well - // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that - // for us internally. - - let (device, mut queues) = Device::new(physical_device, &Features::none(), - &device_extensions(), queue_families) - .expect("failed to create logical device!"); - - self.device = Some(device); - - // TODO!: simplify - self.graphics_queue = queues - .find(|q| q.family().id() == physical_device.queue_families().nth(indices.graphics_family as usize).unwrap().id()); - self.present_queue = queues - .find(|q| q.family().id() == physical_device.queue_families().nth(indices.present_family as usize).unwrap().id()); - } - - fn create_surface(&mut self) { - let instance = self.instance.as_ref().unwrap(); - - let /*mut*/ events_loop = winit::EventsLoop::new(); - self.surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()) - .expect("failed to create window surface!") - .into(); - } - - fn query_swap_chain_support(&self, device: &PhysicalDevice) -> Capabilities { - self.surface.as_ref().unwrap().capabilities(*device) - .expect("failed to get surface capabilities") - } - fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) @@ -267,11 +322,18 @@ impl HelloTriangleApplication { } } - fn create_swap_chain(&mut self) { - let instance = self.instance.as_ref().unwrap(); - let physical_device = PhysicalDevice::from_index(instance, self.physical_device_index).unwrap(); - - let capabilities = self.query_swap_chain_support(&physical_device); + fn create_swap_chain( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + device: &Arc, + graphics_queue: &Arc, + present_queue: &Arc, + old_swapchain: Option>>, + ) -> (Arc>, Vec>>) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let capabilities = surface.capabilities(physical_device) + .expect("failed to get surface capabilities"); let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); @@ -287,19 +349,19 @@ impl HelloTriangleApplication { .. ImageUsage::none() }; - let indices = Self::find_queue_families(&physical_device); + let indices = Self::find_queue_families(&surface, &physical_device); let sharing: SharingMode = if indices.graphics_family != indices.present_family { - vec![self.graphics_queue.as_ref().unwrap(), self.present_queue.as_ref().unwrap()].as_slice().into() + vec![graphics_queue, present_queue].as_slice().into() } else { - self.graphics_queue.as_ref().unwrap().into() + graphics_queue.into() }; let (swap_chain, images) = Swapchain::new( - self.device.as_ref().unwrap().clone(), - self.surface.as_ref().unwrap().clone(), + device.clone(), + surface.clone(), image_count, - surface_format.0, // TODO!? (color space?) + surface_format.0, // TODO: color space? extent, 1, // layers image_usage, @@ -308,29 +370,149 @@ impl HelloTriangleApplication { CompositeAlpha::Opaque, present_mode, true, // clipped - None, // old_swapchain + old_swapchain.as_ref() ).expect("failed to create swap chain!"); - self.swap_chain = Some(swap_chain); - self.swap_chain_images = Some(images); - self.swap_chain_image_format = Some(surface_format.0); - self.swap_chain_extent = Some(extent); - println!("Swapchain created!"); + (swap_chain, images) + } + + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { + Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: color_format, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()) + } + + fn create_graphics_pipeline( + device: &Arc, + swap_chain_extent: [u32; 2], + render_pass: &Arc, + ) -> Arc { + mod vertex_shader { + vulkano_shaders::shader! { + ty: "vertex", + path: "src/bin/17_shader_vertexbuffer.vert" + } + } + + mod fragment_shader { + vulkano_shaders::shader! { + ty: "fragment", + path: "src/bin/17_shader_vertexbuffer.frag" + } + } + + let vert_shader_module = vertex_shader::Shader::load(device.clone()) + .expect("failed to create vertex shader module!"); + let frag_shader_module = fragment_shader::Shader::load(device.clone()) + .expect("failed to create fragment shader module!"); + + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; + let viewport = Viewport { + origin: [0.0, 0.0], + dimensions, + depth_range: 0.0 .. 1.0, + }; + + Arc::new(GraphicsPipeline::start() + .vertex_input_single_buffer::() + .vertex_shader(vert_shader_module.main_entry_point(), ()) + .triangle_list() + .primitive_restart(false) + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport + .fragment_shader(frag_shader_module.main_entry_point(), ()) + .depth_clamp(false) + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... + .polygon_mode_fill() // = default + .line_width(1.0) // = default + .cull_mode_back() + .front_face_clockwise() + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization + .blend_pass_through() // = default + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .build(device.clone()) + .unwrap() + ) + } + + fn create_framebuffers( + swap_chain_images: &[Arc>], + render_pass: &Arc + ) -> Vec> { + swap_chain_images.iter() + .map(|image| { + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()); + fba + } + ).collect::>() + } + + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { + let (buffer, future) = ImmutableBuffer::from_iter( + vertices().iter().cloned(), BufferUsage::vertex_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { + let (buffer, future) = ImmutableBuffer::from_iter( + indices().iter().cloned(), BufferUsage::index_buffer(), + graphics_queue.clone()) + .unwrap(); + future.flush().unwrap(); + buffer + } + + fn create_command_buffers(&mut self) { + let queue_family = self.graphics_queue.family(); + self.command_buffers = self.swap_chain_framebuffers.iter() + .map(|framebuffer| { + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) + .unwrap() + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) + .unwrap() + .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), + vec![self.vertex_buffer.clone()], + self.index_buffer.clone(), (), ()) + .unwrap() + .end_render_pass() + .unwrap() + .build() + .unwrap()) + }) + .collect(); + } + + fn create_sync_objects(device: &Arc) -> Box { + Box::new(sync::now(device.clone())) as Box } - fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { let mut indices = QueueFamilyIndices::new(); // TODO: replace index with id to simplify? for (i, queue_family) in device.queue_families().enumerate() { if queue_family.supports_graphics() { indices.graphics_family = i as i32; + } - // TODO: Vulkano doesn't seem to support querying 'present support' (vkGetPhysicalDeviceSurfaceSupportKHR) - // -> assuming it does if it supports graphics + if surface.is_supported(queue_family).unwrap() { indices.present_family = i as i32; } - if indices.is_complete() { break; } @@ -339,48 +521,122 @@ impl HelloTriangleApplication { indices } - fn check_validation_layer_support() -> bool { - // println!("Available layers:"); - // for layer in instance::layers_list().unwrap() { - // println!("{}", layer.name()); - // } - for layer_name in VALIDATION_LAYERS.iter() { - let mut layer_found = false; - for layer_properties in layers_list().unwrap() { - if *layer_name == layer_properties.name() { - layer_found = true; - break + fn create_logical_device( + instance: &Arc, + surface: &Arc>, + physical_device_index: usize, + ) -> (Arc, Arc, Arc) { + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); + let indices = Self::find_queue_families(&surface, &physical_device); + + let families = [indices.graphics_family, indices.present_family]; + use std::iter::FromIterator; + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); + + let queue_priority = 1.0; + let queue_families = unique_queue_families.iter().map(|i| { + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) + }); + + // NOTE: the tutorial recommends passing the validation layers as well + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that + // for us internally. + + let (device, mut queues) = Device::new(physical_device, &Features::none(), + &device_extensions(), queue_families) + .expect("failed to create logical device!"); + + let graphics_queue = queues.next().unwrap(); + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); + + (device, graphics_queue, present_queue) + } + + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { + let events_loop = EventsLoop::new(); + let surface = WindowBuilder::new() + .with_title("Vulkan") + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) + .build_vk_surface(&events_loop, instance.clone()) + .expect("failed to create window surface!"); + (events_loop, surface) + } + + #[allow(unused)] + fn main_loop(&mut self) { + loop { + self.draw_frame(); + + let mut done = false; + self.events_loop.poll_events(|ev| { + if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { + done = true } - } - if !layer_found { - return false; + }); + if done { + return; } } - - true } - fn get_required_extensions() -> InstanceExtensions { - let mut extensions = vulkano_win::required_extensions(); - if ENABLE_VALIDATION_LAYERS { - // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano - extensions.ext_debug_report = true; + fn draw_frame(&mut self) { + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); + + if self.recreate_swap_chain { + self.recreate_swap_chain(); + self.recreate_swap_chain = false; } - extensions - } + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { + Ok(r) => r, + Err(AcquireError::OutOfDate) => { + self.recreate_swap_chain = true; + return; + }, + Err(err) => panic!("{:?}", err) + }; + + let command_buffer = self.command_buffers[image_index].clone(); - fn main_loop(&self) { + let future = self.previous_frame_end.take().unwrap() + .join(acquire_future) + .then_execute(self.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) + .then_signal_fence_and_flush(); + match future { + Ok(future) => { + self.previous_frame_end = Some(Box::new(future) as Box<_>); + } + Err(vulkano::sync::FlushError::OutOfDate) => { + self.recreate_swap_chain = true; + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + Err(e) => { + println!("{:?}", e); + self.previous_frame_end + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); + } + } } - fn cleanup(&self) { - // TODO!: trust automatic drop and remove or use std::mem::drop here? (instance, device etc.) - // -> check with validation layers for issues with order... + fn recreate_swap_chain(&mut self) { + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); + self.swap_chain = swap_chain; + self.swap_chain_images = images; + + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), + &self.render_pass); + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); + self.create_command_buffers(); } } fn main() { - let mut app = HelloTriangleApplication::new(); - app.run(); + let mut app = HelloTriangleApplication::initialize(); + app.main_loop(); } diff --git a/src/shaders/compile.bat b/src/shaders/compile.bat new file mode 100755 index 0000000..175ddf9 --- /dev/null +++ b/src/shaders/compile.bat @@ -0,0 +1,2 @@ +glslangValidator -V shader.vert +glslangValidator -V shader.frag diff --git a/src/shaders/compile.sh b/src/shaders/compile.sh new file mode 100755 index 0000000..326bd2a --- /dev/null +++ b/src/shaders/compile.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source ../../mac-env.sh +glslangValidator -V shader.vert +glslangValidator -V shader.frag diff --git a/src/shaders/shader.frag b/src/shaders/shader.frag new file mode 100644 index 0000000..84daf5e --- /dev/null +++ b/src/shaders/shader.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} diff --git a/src/shaders/shader.vert b/src/shaders/shader.vert new file mode 100644 index 0000000..3b48be4 --- /dev/null +++ b/src/shaders/shader.vert @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +// NOTE: names must match the `Vertex` struct in Rust +layout(location = 0) in vec2 pos; +layout(location = 1) in vec3 color; + +layout(location = 0) out vec3 fragColor; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() { + gl_Position = vec4(pos, 0.0, 1.0); + fragColor = color; +}