From 348a06ee0f8831d0973d2a7a677704c98e450b56 Mon Sep 17 00:00:00 2001 From: Ben Hansen Date: Tue, 12 Nov 2019 23:52:09 -0700 Subject: [PATCH] reorganized project, added challenge impls --- Cargo.lock | 477 +++++++++++++----- Cargo.toml | 10 +- code/Cargo.toml | 42 -- code/beginner/tutorial1-window/Cargo.toml | 19 + .../tutorial1-window/main.rs} | 0 code/beginner/tutorial2-swapchain/Cargo.toml | 22 + .../beginner/tutorial2-swapchain/challenge.rs | 177 +++++++ .../tutorial2-swapchain/main.rs} | 17 +- code/beginner/tutorial3-pipeline/Cargo.toml | 22 + .../tutorial3-pipeline/challenge.frag | 8 + code/beginner/tutorial3-pipeline/challenge.rs | 286 +++++++++++ .../tutorial3-pipeline/challenge.vert | 14 + .../beginner/tutorial3-pipeline/main.rs | 17 +- .../beginner/tutorial3-pipeline/shader.frag | 0 .../beginner/tutorial3-pipeline/shader.vert | 0 code/beginner/tutorial4-buffer/Cargo.toml | 22 + code/beginner/tutorial4-buffer/challenge.rs | 346 +++++++++++++ .../beginner/tutorial4-buffer/main.rs | 37 +- .../beginner/tutorial4-buffer/shader.frag | 0 .../beginner/tutorial4-buffer/shader.vert | 0 code/beginner/tutorial5-textures/Cargo.toml | 23 + code/beginner/tutorial5-textures/challenge.rs | 436 ++++++++++++++++ .../tutorial5-textures/happy-tree-cartoon.png | Bin 0 -> 59874 bytes .../tutorial5-textures/happy-tree.png | Bin 0 -> 28134 bytes .../beginner/tutorial5-textures/main.rs | 41 +- .../beginner/tutorial5-textures/shader.frag | 0 .../beginner/tutorial5-textures/shader.vert | 0 code/intermediate/windowless/Cargo.toml | 19 + .../{src => }/intermediate/windowless/main.rs | 9 +- .../intermediate/windowless/shader.frag | 0 .../intermediate/windowless/shader.vert | 0 .../tutorial5-textures/happy-tree.png | Bin 2278 -> 0 bytes code/src/lib.rs | 7 - docs/.vuepress/components/RecentArticles.vue | 37 ++ docs/.vuepress/config.js | 6 +- docs/.vuepress/dist | 2 +- docs/README.md | 2 +- docs/beginner/README.md | 4 - docs/beginner/tutorial1-window.md | 14 +- .../README.md} | 25 +- .../cleared-window.png} | Bin docs/beginner/tutorial3-pipeline/README.md | 4 +- docs/beginner/tutorial4-buffer/README.md | 5 +- docs/beginner/tutorial5-textures/README.md | 29 +- .../tutorial5-textures/address_mode.png | Bin 0 -> 32325 bytes .../tutorial5-textures/address_mode.xcf | Bin 0 -> 210852 bytes .../happy-tree-uv-coords.png | Bin 0 -> 29665 bytes .../happy-tree-uv-coords.xcf | Bin 0 -> 141258 bytes .../tutorial5-textures/happy-tree.png | Bin 2278 -> 28134 bytes .../tutorial5-textures/happy-tree.xcf | Bin 15703 -> 300448 bytes .../tutorial5-textures/rightside-up.png | Bin 13831 -> 33015 bytes .../tutorial5-textures/upside-down.png | Bin 12466 -> 30567 bytes docs/intermediate/image.png | Bin 1508 -> 0 bytes .../{windowless.md => windowless/README.md} | 0 docs/news/README.md | 25 + 55 files changed, 1926 insertions(+), 278 deletions(-) delete mode 100644 code/Cargo.toml create mode 100644 code/beginner/tutorial1-window/Cargo.toml rename code/{src/beginner/tutorial1-window.rs => beginner/tutorial1-window/main.rs} (100%) create mode 100644 code/beginner/tutorial2-swapchain/Cargo.toml create mode 100644 code/beginner/tutorial2-swapchain/challenge.rs rename code/{src/beginner/tutorial2-swapchain.rs => beginner/tutorial2-swapchain/main.rs} (91%) create mode 100644 code/beginner/tutorial3-pipeline/Cargo.toml create mode 100644 code/beginner/tutorial3-pipeline/challenge.frag create mode 100644 code/beginner/tutorial3-pipeline/challenge.rs create mode 100644 code/beginner/tutorial3-pipeline/challenge.vert rename code/{src => }/beginner/tutorial3-pipeline/main.rs (94%) rename code/{src => }/beginner/tutorial3-pipeline/shader.frag (100%) rename code/{src => }/beginner/tutorial3-pipeline/shader.vert (100%) create mode 100644 code/beginner/tutorial4-buffer/Cargo.toml create mode 100644 code/beginner/tutorial4-buffer/challenge.rs rename code/{src => }/beginner/tutorial4-buffer/main.rs (85%) rename code/{src => }/beginner/tutorial4-buffer/shader.frag (100%) rename code/{src => }/beginner/tutorial4-buffer/shader.vert (100%) create mode 100644 code/beginner/tutorial5-textures/Cargo.toml create mode 100644 code/beginner/tutorial5-textures/challenge.rs create mode 100644 code/beginner/tutorial5-textures/happy-tree-cartoon.png create mode 100644 code/beginner/tutorial5-textures/happy-tree.png rename code/{src => }/beginner/tutorial5-textures/main.rs (93%) rename code/{src => }/beginner/tutorial5-textures/shader.frag (100%) rename code/{src => }/beginner/tutorial5-textures/shader.vert (100%) create mode 100644 code/intermediate/windowless/Cargo.toml rename code/{src => }/intermediate/windowless/main.rs (95%) rename code/{src => }/intermediate/windowless/shader.frag (100%) rename code/{src => }/intermediate/windowless/shader.vert (100%) delete mode 100644 code/src/beginner/tutorial5-textures/happy-tree.png delete mode 100644 code/src/lib.rs create mode 100644 docs/.vuepress/components/RecentArticles.vue delete mode 100644 docs/beginner/README.md rename docs/beginner/{tutorial2-swapchain.md => tutorial2-swapchain/README.md} (91%) rename docs/beginner/{tutorial2-swapchain-cleared-window.png => tutorial2-swapchain/cleared-window.png} (100%) create mode 100644 docs/beginner/tutorial5-textures/address_mode.png create mode 100644 docs/beginner/tutorial5-textures/address_mode.xcf create mode 100644 docs/beginner/tutorial5-textures/happy-tree-uv-coords.png create mode 100644 docs/beginner/tutorial5-textures/happy-tree-uv-coords.xcf delete mode 100644 docs/intermediate/image.png rename docs/intermediate/{windowless.md => windowless/README.md} (100%) create mode 100644 docs/news/README.md diff --git a/Cargo.lock b/Cargo.lock index 12da17b2..96a8c9da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,26 +67,6 @@ name = "autocfg" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "backtrace" -version = "0.3.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bitflags" version = "1.2.1" @@ -106,6 +86,11 @@ dependencies = [ "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bumpalo" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-tools" version = "0.2.0" @@ -173,7 +158,7 @@ dependencies = [ [[package]] name = "cocoa" -version = "0.18.4" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -185,18 +170,6 @@ dependencies = [ "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "code" -version = "0.1.0" -dependencies = [ - "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wgpu 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winit 0.20.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "color_quant" version = "1.0.1" @@ -297,22 +270,22 @@ dependencies = [ ] [[package]] -name = "deflate" -version = "0.7.20" +name = "d3d12" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "derivative" -version = "1.0.3" +name = "deflate" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -346,26 +319,6 @@ name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "failure" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -411,6 +364,11 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "generic-array" version = "0.9.0" @@ -429,29 +387,101 @@ dependencies = [ "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gfx-auxil" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "spirv_cross 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gfx-backend-dx11" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-auxil 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "range-alloc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "spirv_cross 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gfx-backend-dx12" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "d3d12 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-auxil 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "range-alloc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "spirv_cross 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gfx-backend-empty" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gfx-backend-metal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "copyless 0.1.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)", + "gfx-auxil 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "metal 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "range-alloc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "spirv_cross 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "storage-map 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gfx-backend-vulkan" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "ash 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -459,12 +489,12 @@ dependencies = [ [[package]] name = "gfx-hal" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -518,6 +548,11 @@ dependencies = [ "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "instant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "iovec" version = "0.1.4" @@ -535,6 +570,14 @@ dependencies = [ "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "js-sys" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -627,6 +670,20 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "metal" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.19.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)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "mio" version = "0.6.19" @@ -754,6 +811,15 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "objc_exception 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "objc_exception" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -979,6 +1045,11 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "range-alloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "raw-window-handle" version = "0.1.2" @@ -987,6 +1058,14 @@ dependencies = [ "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "raw-window-handle" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rayon" version = "1.2.0" @@ -1027,7 +1106,6 @@ name = "relevant" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1042,12 +1120,10 @@ dependencies = [ [[package]] name = "rendy-descriptor" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "relevant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1055,13 +1131,11 @@ dependencies = [ [[package]] name = "rendy-memory" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "colorful 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "relevant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1069,11 +1143,6 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rustc_version" version = "0.2.3" @@ -1177,6 +1246,16 @@ dependencies = [ "wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "spirv_cross" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.31 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "stb_truetype" version = "0.3.0" @@ -1185,6 +1264,14 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "storage-map" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.44" @@ -1216,17 +1303,6 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "synstructure" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tempfile" version = "3.1.0" @@ -1251,6 +1327,61 @@ dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tutorial1-window" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tutorial2-swapchain" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tutorial3-pipeline" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tutorial4-buffer" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tutorial5-textures" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.11.2" @@ -1291,6 +1422,55 @@ name = "wasi" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasm-bindgen" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "wayland-client" version = "0.23.6" @@ -1348,32 +1528,37 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wgpu-native 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu-native 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "zerocopy 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wgpu-native" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-backend-empty 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-backend-vulkan 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-dx11 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-dx12 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-empty 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-metal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-backend-vulkan 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rendy-descriptor 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rendy-memory 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rendy-descriptor 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rendy-memory 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1414,33 +1599,53 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "windowless" +version = "0.1.0" +dependencies = [ + "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winit" -version = "0.20.0-alpha3" +version = "0.20.0-alpha4" 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)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-video-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "instant 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -1510,11 +1715,10 @@ dependencies = [ "checksum ash 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "003d1fb2eb12eb06d4a03dbe02eea67a9fac910fa97932ab9e3a75b96a1ea5e5" "checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "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 bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" @@ -1524,7 +1728,7 @@ dependencies = [ "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.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" -"checksum cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf79daa4e11e5def06e55306aa3601b87de6b5149671529318da048f67cdd77b" +"checksum cocoa 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400" "checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" "checksum colorful 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65" "checksum copyless 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127" @@ -1537,15 +1741,13 @@ dependencies = [ "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum d3d12 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc7ed48e89905e5e146bcc1951cc3facb9e44aea9adf5dc01078cda1bd24b662" "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" -"checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e93ca78226c51902d7aa8c12c988338aadd9e85ed9c6be8aaac39192ff3605" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5fe414cc2fd4447b7da94b27ddfb6831a8a06f35f6d077ab5613ec703866c49a" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "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" @@ -1553,18 +1755,25 @@ dependencies = [ "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 fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" -"checksum gfx-backend-empty 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf40d1cd04f92f8555db32dd9fb5bb13d17ae4624318b73604b4186b1d66f0a" -"checksum gfx-backend-vulkan 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5facf0b6e4306a6a93682077f85602c89b49aca8ecceb73b9558e7baa37f8c54" -"checksum gfx-hal 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00a079b66250a6437c3822c8bfc25e5bed5ba0186d312e7df87bc503325ab33c" +"checksum gfx-auxil 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "572eee952a9a23c99cfe3e4fd95d277784058a89ac3c77ff6fa3d80a4e321919" +"checksum gfx-backend-dx11 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c77836ff26cf9916e5c8745715a22eae1fc61d994ffa0bea8a7dbd708ece2" +"checksum gfx-backend-dx12 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e913cc800fb12eaba2c420091a02aca9aafbefd672600dfc5b52654343d341" +"checksum gfx-backend-empty 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d383e6bc48867cb37d298a20139fd1eec298f8f6d594690cd1c50ef25470cc7" +"checksum gfx-backend-metal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8de5c71f18ba805c95b84d6c78c472ef44485a6fc46e3b49fe1e6739c8d7b0c0" +"checksum gfx-backend-vulkan 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62538fedd66a78968a162e8e1a29d085ffbc97f8782634684b2f7da7aea59207" +"checksum gfx-hal 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7c88981665c780447bb08eb099e1ded330754a7246719bab927ee4a949c0ba7f" "checksum gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" "checksum glsl-to-spirv 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "28caebc98746d507603a2d3df66dcbe04e41d4febad0320f3eec1ef72b6bbef1" "checksum hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47e7292fd9f7fe89fa35c98048f2d0a69b79ed243604234d18f6f8a1aa6f408d" "checksum image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4be8aaefbe7545dc42ae925afb55a0098f226a3fe5ef721872806f44f57826" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +"checksum instant 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum jpeg-decoder 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "c1aae18ffeeae409c6622c3b6a7ee49792a7e5a062eea1b135fbb74e301792ba" +"checksum js-sys 0.3.31 (registry+https://github.com/rust-lang/crates.io-index)" = "d8657b7ca06a6044ece477f6900bf7670f8b5fd0cce177a1d7094eef51e0adf4" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" @@ -1578,6 +1787,7 @@ dependencies = [ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum metal 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf8052f20601c7af6293d3f7bf7b9159aee5974804fe65d871d437f933ec1eb" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" @@ -1591,6 +1801,7 @@ dependencies = [ "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +"checksum objc_exception 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "098cd29a2fa3c230d3463ae069cecccc3fdfd64c0d2496ab5b96f82dab6a00dc" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" @@ -1616,16 +1827,17 @@ dependencies = [ "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum range-alloc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5927936723a9e8b715d37d7e4b390455087c4bdf25b9f702309460577b14f9" "checksum raw-window-handle 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "af3d3b2e1053b3ff2171efc29a8bff3439ce6b2ce6a0432695134bc1c7ff8e87" +"checksum raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9db80d08d3ed847ce4fb3def46de0af4bfb6155bd09bd6eaf28b5ac72541c1f1" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum relevant 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bbc232e13d37f4547f5b9b42a5efc380cabe5dbc1807f8b893580640b2ab0308" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rendy-descriptor 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca7bcc3cb86a7945ecc5f0d7121e47a0b5979c3c57d3a5e6facc8738338651d8" -"checksum rendy-memory 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cf6b3fc8a012b69062419caf086d35f83d9af57bf30a6971691731b4816a47f" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rendy-descriptor 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f475bcc0505946e998590f1f0545c52ef4b559174a1b353a7ce6638def8b621e" +"checksum rendy-memory 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed492161a819feae7f27f418bb16035276ac20649c60d756699152cb5c1960ec" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" "checksum rusttype 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa38506b5cbf2fb67f915e2725cb5012f1b9a785b0ab55c4733acda5f6554ef" @@ -1639,11 +1851,12 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054" +"checksum spirv_cross 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fbbe441b3ac8ec0ae6a4f05234239bd372a241ce15793eef694e8b24afc267bb" "checksum stb_truetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "824210d6fb52cbc3ad2545270ead6860c7311aa5450642b078da4515937b6f7a" +"checksum storage-map 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0a4829a5c591dc24a944a736d6b1e4053e51339a79fd5d4702c4c999a9c45e" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" -"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b7c2cfc4742bd8a32f2e614339dd8ce30dbcf676bb262bd63a2327bc5df57d" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" @@ -1653,20 +1866,26 @@ dependencies = [ "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasm-bindgen 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c4568ae1b4e07ca907b1a4de41174eaa3e5be4066c024475586b7842725f69a9" +"checksum wasm-bindgen-backend 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5a00cfdce37367770062065fd3abb9278cbae86a0d918cacd0978a7acd51b481" +"checksum wasm-bindgen-macro 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7c568f4d3cf6d7c1d72b165daf778fb0d6e09a24f96ac14fc8c4f66a96e86b72" +"checksum wasm-bindgen-macro-support 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "430d12539ae324d16097b399e9d07a6d5ce0173b2a61a2d02346ca7c198daffe" +"checksum wasm-bindgen-shared 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "8ae7167f0bbffd7fac2b12da0fa1f834c1d84671a1ae3c93ac8bde2e97179c39" "checksum wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" "checksum wayland-commons 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" "checksum wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" "checksum wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" "checksum wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" -"checksum wgpu 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "811952b5c4404d0344b5b5af74ed61ba11d4a2bef4ce5dae31fc25def21c7a25" -"checksum wgpu-native 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdb3ad9009d231cfa5a68ca58f28c5f12c4b86570ef8777e2ebac1a4adc8373" +"checksum wgpu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e9c1ff587eddd68cdf2a78889c7a2128683161c72c67b94457cf498accaf7b" +"checksum wgpu-native 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d94a72d634c7c69f37da2bf9adc1d801a7504ad784cc03dc254902bd720c84c" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "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.20.0-alpha3 (registry+https://github.com/rust-lang/crates.io-index)" = "27c947714bd09779bb28fb7ac85685d5b68ab60d17349ed0ea7ae9484726024d" +"checksum winit 0.20.0-alpha4 (registry+https://github.com/rust-lang/crates.io-index)" = "56c565622ccb05351d92445415952ca09dade6a53e75dd9e75d9bd35d4e99333" +"checksum wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39697e3123f715483d311b5826e254b6f3cfebdd83cf7ef3358f579c3d68e235" "checksum x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "be65e1342a3baae65439cd03306778831a3d133b0d20243a7fb83fd5cf403c58" diff --git a/Cargo.toml b/Cargo.toml index a9336f5c..c5787112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,12 @@ [workspace] members = [ - "code" + # beginner tutorials + "code/beginner/tutorial1-window", + "code/beginner/tutorial2-swapchain", + "code/beginner/tutorial3-pipeline", + "code/beginner/tutorial4-buffer", + "code/beginner/tutorial5-textures", + + # intermediate tutorials + "code/intermediate/windowless", ] \ No newline at end of file diff --git a/code/Cargo.toml b/code/Cargo.toml deleted file mode 100644 index bc589d3b..00000000 --- a/code/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "code" -version = "0.1.0" -authors = ["Ben Hansen "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[[bin]] -name = "tutorial1-window" -path = "src/beginner/tutorial1-window.rs" - -[[bin]] -name = "tutorial2-swapchain" -path = "src/beginner/tutorial2-swapchain.rs" - -[[bin]] -name = "tutorial3-pipeline" -path = "src/beginner/tutorial3-pipeline/main.rs" - -[[bin]] -name = "tutorial4-buffer" -path = "src/beginner/tutorial4-buffer/main.rs" - -[[bin]] -name = "tutorial5-textures" -path = "src/beginner/tutorial5-textures/main.rs" - -[[bin]] -name = "windowless" -path = "src/intermediate/windowless/main.rs" - -[dependencies] -image = "0.22" -raw-window-handle = "0.1" -winit = "0.20.0-alpha3" -glsl-to-spirv = "0.1" -cgmath = "0.17" - -[dependencies.wgpu] -version = "0.3" -features = ["vulkan"] \ No newline at end of file diff --git a/code/beginner/tutorial1-window/Cargo.toml b/code/beginner/tutorial1-window/Cargo.toml new file mode 100644 index 00000000..34ee25cd --- /dev/null +++ b/code/beginner/tutorial1-window/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tutorial1-window" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[[bin]] +name = "tutorial1-window" +path = "main.rs" + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha4" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" diff --git a/code/src/beginner/tutorial1-window.rs b/code/beginner/tutorial1-window/main.rs similarity index 100% rename from code/src/beginner/tutorial1-window.rs rename to code/beginner/tutorial1-window/main.rs diff --git a/code/beginner/tutorial2-swapchain/Cargo.toml b/code/beginner/tutorial2-swapchain/Cargo.toml new file mode 100644 index 00000000..2779ee98 --- /dev/null +++ b/code/beginner/tutorial2-swapchain/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tutorial2-swapchain" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha4" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" + +[[bin]] +name = "tutorial2-swapchain" +path = "main.rs" + +[[bin]] +name = "tutorial2-challenge" +path = "challenge.rs" diff --git a/code/beginner/tutorial2-swapchain/challenge.rs b/code/beginner/tutorial2-swapchain/challenge.rs new file mode 100644 index 00000000..a8107086 --- /dev/null +++ b/code/beginner/tutorial2-swapchain/challenge.rs @@ -0,0 +1,177 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + clear_color: wgpu::Color, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let clear_color = wgpu::Color::BLACK; + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + + clear_color, + + hidpi_factor, + size, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + } + + fn input(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::CursorMoved { + position, + .. + } => { + self.clear_color = wgpu::Color { + r: position.x as f64 / self.size.width as f64, + g: position.y as f64 / self.size.height as f64, + b: 1.0, + a: 1.0, + }; + true + } + _ => false, + } + } + + fn update(&mut self) { + + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: self.clear_color, + } + ], + depth_stencil_attachment: None, + }); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/src/beginner/tutorial2-swapchain.rs b/code/beginner/tutorial2-swapchain/main.rs similarity index 91% rename from code/src/beginner/tutorial2-swapchain.rs rename to code/beginner/tutorial2-swapchain/main.rs index b7a3b847..498c9f4f 100644 --- a/code/src/beginner/tutorial2-swapchain.rs +++ b/code/beginner/tutorial2-swapchain/main.rs @@ -7,6 +7,7 @@ use winit::{ struct State { surface: wgpu::Surface, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -20,16 +21,13 @@ impl State { let size = window.inner_size(); let physical_size = size.to_physical(hidpi_factor); - let instance = wgpu::Instance::new(); + let surface = wgpu::Surface::create(window); - use raw_window_handle::HasRawWindowHandle as _; - let surface = instance.create_surface(window.raw_window_handle()); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: Default::default(), - }); - - let device = adapter.request_device(&wgpu::DeviceDescriptor { + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, @@ -48,6 +46,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, hidpi_factor, @@ -103,7 +102,7 @@ impl State { }); } - self.device.get_queue().submit(&[ + self.queue.submit(&[ encoder.finish() ]); } diff --git a/code/beginner/tutorial3-pipeline/Cargo.toml b/code/beginner/tutorial3-pipeline/Cargo.toml new file mode 100644 index 00000000..d431f8a0 --- /dev/null +++ b/code/beginner/tutorial3-pipeline/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tutorial3-pipeline" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha4" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" + +[[bin]] +name = "tutorial3-pipeline" +path = "main.rs" + +[[bin]] +name = "tutorial3-challenge" +path = "challenge.rs" \ No newline at end of file diff --git a/code/beginner/tutorial3-pipeline/challenge.frag b/code/beginner/tutorial3-pipeline/challenge.frag new file mode 100644 index 00000000..e7c5feda --- /dev/null +++ b/code/beginner/tutorial3-pipeline/challenge.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location=0) in vec2 v_position; +layout(location=0) out vec4 f_color; + +void main() { + f_color = vec4(v_position, 0.5, 1.0); +} \ No newline at end of file diff --git a/code/beginner/tutorial3-pipeline/challenge.rs b/code/beginner/tutorial3-pipeline/challenge.rs new file mode 100644 index 00000000..b3edc97a --- /dev/null +++ b/code/beginner/tutorial3-pipeline/challenge.rs @@ -0,0 +1,286 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + challenge_render_pipeline: wgpu::RenderPipeline, + use_color: bool, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let vs_src = include_str!("shader.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vs_src = include_str!("challenge.vert"); + let fs_src = include_str!("challenge.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let challenge_render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[], + }); + + let challenge_render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &challenge_render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let use_color = true; + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + challenge_render_pipeline, + use_color, + hidpi_factor, + size, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + } + + fn input(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some( + VirtualKeyCode::Space + ), + .. + }, + .. + } => { + self.use_color = *state == ElementState::Released; + true + } + _ => false, + } + } + + fn update(&mut self) { + + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(if self.use_color { + &self.render_pipeline + } else { + &self.challenge_render_pipeline + }); + render_pass.draw(0..3, 0..1); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial3-pipeline/challenge.vert b/code/beginner/tutorial3-pipeline/challenge.vert new file mode 100644 index 00000000..3b96f434 --- /dev/null +++ b/code/beginner/tutorial3-pipeline/challenge.vert @@ -0,0 +1,14 @@ +#version 450 + +const vec2 positions[3] = vec2[3]( + vec2(0.0, -0.5), + vec2(-0.5, 0.5), + vec2(0.5, 0.5) +); + +layout(location=0) out vec2 v_position; + +void main() { + v_position = positions[gl_VertexIndex]; + gl_Position = vec4(v_position, 0.0, 1.0); +} \ No newline at end of file diff --git a/code/src/beginner/tutorial3-pipeline/main.rs b/code/beginner/tutorial3-pipeline/main.rs similarity index 94% rename from code/src/beginner/tutorial3-pipeline/main.rs rename to code/beginner/tutorial3-pipeline/main.rs index fe86b576..86355bfa 100644 --- a/code/src/beginner/tutorial3-pipeline/main.rs +++ b/code/beginner/tutorial3-pipeline/main.rs @@ -7,6 +7,7 @@ use winit::{ struct State { surface: wgpu::Surface, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -22,16 +23,13 @@ impl State { let size = window.inner_size(); let physical_size = size.to_physical(hidpi_factor); - let instance = wgpu::Instance::new(); + let surface = wgpu::Surface::create(window); - use raw_window_handle::HasRawWindowHandle as _; - let surface = instance.create_surface(window.raw_window_handle()); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: Default::default(), - }); - - let device = adapter.request_device(&wgpu::DeviceDescriptor { + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, @@ -97,6 +95,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -156,7 +155,7 @@ impl State { render_pass.draw(0..3, 0..1); } - self.device.get_queue().submit(&[ + self.queue.submit(&[ encoder.finish() ]); } diff --git a/code/src/beginner/tutorial3-pipeline/shader.frag b/code/beginner/tutorial3-pipeline/shader.frag similarity index 100% rename from code/src/beginner/tutorial3-pipeline/shader.frag rename to code/beginner/tutorial3-pipeline/shader.frag diff --git a/code/src/beginner/tutorial3-pipeline/shader.vert b/code/beginner/tutorial3-pipeline/shader.vert similarity index 100% rename from code/src/beginner/tutorial3-pipeline/shader.vert rename to code/beginner/tutorial3-pipeline/shader.vert diff --git a/code/beginner/tutorial4-buffer/Cargo.toml b/code/beginner/tutorial4-buffer/Cargo.toml new file mode 100644 index 00000000..355922bc --- /dev/null +++ b/code/beginner/tutorial4-buffer/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tutorial4-buffer" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha4" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" + +[[bin]] +name = "tutorial4-buffer" +path = "main.rs" + +[[bin]] +name = "tutorial4-challenge" +path = "challenge.rs" \ No newline at end of file diff --git a/code/beginner/tutorial4-buffer/challenge.rs b/code/beginner/tutorial4-buffer/challenge.rs new file mode 100644 index 00000000..d60c0256 --- /dev/null +++ b/code/beginner/tutorial4-buffer/challenge.rs @@ -0,0 +1,346 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + color: [f32; 3], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float3, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], color: [0.5, 0.0, 0.5] }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + challenge_vertex_buffer: wgpu::Buffer, + challenge_index_buffer: wgpu::Buffer, + num_challenge_indices: u32, + use_complex: bool, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let vs_src = include_str!("shader.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + let num_vertices = 16; + let angle = std::f32::consts::PI * 2.0 / num_vertices as f32; + let challenge_verts = (0..num_vertices).map(|i| { + let theta = angle * i as f32; + Vertex { + position: [ + 0.5 * theta.cos(), + 0.5 * theta.sin(), + 0.0, + ], + color: [ + (1.0 + theta.cos()) / 2.0, + (1.0 + theta.sin()) / 2.0, + 1.0, + ] + } + }).collect::>(); + + let num_triangles = num_vertices - 2; + let challenge_indices = (1u16..num_triangles+1).into_iter().flat_map(|i| { + vec![i + 1, i, 0] + }).collect::>(); + let num_challenge_indices = challenge_indices.len() as u32; + + let challenge_vertex_buffer = device + .create_buffer_mapped(challenge_verts.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(&challenge_verts); + let challenge_index_buffer = device + .create_buffer_mapped(challenge_indices.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(&challenge_indices); + + let use_complex = false; + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + challenge_vertex_buffer, + challenge_index_buffer, + num_challenge_indices, + use_complex, + hidpi_factor, + size, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + } + + fn input(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(VirtualKeyCode::Space), + .. + }, + .. + } => { + self.use_complex = *state == ElementState::Pressed; + true + } + _ => false, + } + } + + fn update(&mut self) { + + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.render_pipeline); + + let data = if self.use_complex { + ( + &self.challenge_vertex_buffer, + &self.challenge_index_buffer, + self.num_challenge_indices + ) + } else { + ( + &self.vertex_buffer, + &self.index_buffer, + self.num_indices, + ) + }; + render_pass.set_vertex_buffers(0, &[(data.0, 0)]); + render_pass.set_index_buffer(data.1, 0); + + render_pass.draw_indexed(0..data.2, 0, 0..1); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/src/beginner/tutorial4-buffer/main.rs b/code/beginner/tutorial4-buffer/main.rs similarity index 85% rename from code/src/beginner/tutorial4-buffer/main.rs rename to code/beginner/tutorial4-buffer/main.rs index 97e04f8c..1a6c7ce7 100644 --- a/code/src/beginner/tutorial4-buffer/main.rs +++ b/code/beginner/tutorial4-buffer/main.rs @@ -33,26 +33,6 @@ impl Vertex { } } -// const VERTICES: &[Vertex] = &[ -// Vertex { position: [0.0, -0.5, 0.0], color: [1.0, 0.0, 0.0] }, -// Vertex { position: [-0.5, 0.5, 0.0], color: [0.0, 1.0, 0.0] }, -// Vertex { position: [0.5, 0.5, 0.0], color: [0.0, 0.0, 1.0] }, -// ]; - -// const VERTICES: &[Vertex] = &[ -// Vertex { position: [-0.0868241, -0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A -// Vertex { position: [-0.49513406, -0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B -// Vertex { position: [0.44147372, -0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E - -// Vertex { position: [-0.49513406, -0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B -// Vertex { position: [-0.21918549, 0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C -// Vertex { position: [0.44147372, -0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E - -// Vertex { position: [-0.21918549, 0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C -// Vertex { position: [0.35966998, 0.3473291, 0.0], color: [0.5, 0.0, 0.5] }, // D -// Vertex { position: [0.44147372, -0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E -// ]; - const VERTICES: &[Vertex] = &[ Vertex { position: [-0.0868241, -0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A Vertex { position: [-0.49513406, -0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B @@ -70,6 +50,7 @@ const INDICES: &[u16] = &[ struct State { surface: wgpu::Surface, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -89,16 +70,13 @@ impl State { let size = window.inner_size(); let physical_size = size.to_physical(hidpi_factor); - let instance = wgpu::Instance::new(); - - use raw_window_handle::HasRawWindowHandle as _; - let surface = instance.create_surface(window.raw_window_handle()); + let surface = wgpu::Surface::create(window); - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: Default::default(), - }); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); - let device = adapter.request_device(&wgpu::DeviceDescriptor { + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, @@ -174,6 +152,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -238,7 +217,7 @@ impl State { render_pass.draw_indexed(0..self.num_indices, 0, 0..1); } - self.device.get_queue().submit(&[ + self.queue.submit(&[ encoder.finish() ]); } diff --git a/code/src/beginner/tutorial4-buffer/shader.frag b/code/beginner/tutorial4-buffer/shader.frag similarity index 100% rename from code/src/beginner/tutorial4-buffer/shader.frag rename to code/beginner/tutorial4-buffer/shader.frag diff --git a/code/src/beginner/tutorial4-buffer/shader.vert b/code/beginner/tutorial4-buffer/shader.vert similarity index 100% rename from code/src/beginner/tutorial4-buffer/shader.vert rename to code/beginner/tutorial4-buffer/shader.vert diff --git a/code/beginner/tutorial5-textures/Cargo.toml b/code/beginner/tutorial5-textures/Cargo.toml new file mode 100644 index 00000000..a1edde62 --- /dev/null +++ b/code/beginner/tutorial5-textures/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tutorial5-textures" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +winit = "0.20.0-alpha3" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" + +[[bin]] +name = "tutorial5-textures" +path = "main.rs" + + +[[bin]] +name = "tutorial5-challenge" +path = "challenge.rs" \ No newline at end of file diff --git a/code/beginner/tutorial5-textures/challenge.rs b/code/beginner/tutorial5-textures/challenge.rs new file mode 100644 index 00000000..f04cf98d --- /dev/null +++ b/code/beginner/tutorial5-textures/challenge.rs @@ -0,0 +1,436 @@ +use winit::{ + event::*, + event_loop::{EventLoop, ControlFlow}, + window::{Window, WindowBuilder}, +}; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct Vertex { + position: [f32; 3], + tex_coords: [f32; 2], +} + +impl Vertex { + fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> { + use std::mem; + wgpu::VertexBufferDescriptor { + stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &[ + wgpu::VertexAttributeDescriptor { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float3, + }, + wgpu::VertexAttributeDescriptor { + offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float2, + }, + ] + } + } +} + +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E +]; + +const INDICES: &[u16] = &[ + 0, 1, 4, + 1, 2, 4, + 2, 3, 4, +]; + + +pub struct Texture { + pub texture: wgpu::Texture, + pub view: wgpu::TextureView, + pub sampler: wgpu::Sampler, + pub bind_group: wgpu::BindGroup, +} + +impl Texture { + pub fn from_bytes( + device: &wgpu::Device, + queue: &mut wgpu::Queue, + layout: &wgpu::BindGroupLayout, + data: &[u8], + ) -> Self { + let image = image::load_from_memory(data).unwrap(); + let rgba = image.as_rgba8().unwrap(); + + use image::GenericImageView; + let dimensions = image.dimensions(); + + let size3d = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth: 1, + }; + let texture = device.create_texture(&wgpu::TextureDescriptor { + size: size3d, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, + }); + + let diffuse_buffer = device + .create_buffer_mapped(rgba.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&rgba); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &diffuse_buffer, + offset: 0, + row_pitch: 4 * dimensions.0, + image_height: dimensions.1, + }, + wgpu::TextureCopyView { + texture: &texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + size3d, + ); + + queue.submit(&[encoder.finish()]); + + let view = texture.create_default_view(); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &layout, + bindings: &[ + wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&view), + }, + wgpu::Binding { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + } + ], + }); + + Self { + texture, + view, + sampler, + bind_group, + } + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + sc_desc: wgpu::SwapChainDescriptor, + swap_chain: wgpu::SwapChain, + + render_pipeline: wgpu::RenderPipeline, + + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + num_indices: u32, + + diffuse_texture: Texture, + cartoon_texture: Texture, + + hidpi_factor: f64, + size: winit::dpi::LogicalSize, + + is_space_pressed: bool, +} + +impl State { + fn new(window: &Window) -> Self { + let hidpi_factor = window.hidpi_factor(); + let size = window.inner_size(); + let physical_size = size.to_physical(hidpi_factor); + + let surface = wgpu::Surface::create(window); + + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: Default::default(), + }); + + let sc_desc = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: physical_size.width.round() as u32, + height: physical_size.height.round() as u32, + present_mode: wgpu::PresentMode::Vsync, + }; + let swap_chain = device.create_swap_chain(&surface, &sc_desc); + + let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[ + wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }, + wgpu::BindGroupLayoutBinding { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }, + ], + }); + + let diffuse_bytes = include_bytes!("happy-tree.png"); + let diffuse_texture = Texture::from_bytes( + &device, + &mut queue, + &texture_bind_group_layout, + diffuse_bytes, + ); + + let cartoon_bytes = include_bytes!("happy-tree-cartoon.png"); + let cartoon_texture = Texture::from_bytes( + &device, + &mut queue, + &texture_bind_group_layout, + cartoon_bytes, + ); + + let vs_src = include_str!("shader.vert"); + let fs_src = include_str!("shader.frag"); + let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap(); + let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap(); + let vs_data = wgpu::read_spirv(vs_spirv).unwrap(); + let fs_data = wgpu::read_spirv(fs_spirv).unwrap(); + let vs_module = device.create_shader_module(&vs_data); + let fs_module = device.create_shader_module(&fs_data); + + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&texture_bind_group_layout], + }); + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &render_pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[ + wgpu::ColorStateDescriptor { + format: sc_desc.format, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }, + ], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[ + Vertex::desc(), + ], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + let vertex_buffer = device + .create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX) + .fill_from_slice(VERTICES); + let index_buffer = device + .create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX) + .fill_from_slice(INDICES); + let num_indices = INDICES.len() as u32; + + Self { + surface, + device, + queue, + sc_desc, + swap_chain, + render_pipeline, + vertex_buffer, + index_buffer, + num_indices, + diffuse_texture, + cartoon_texture, + hidpi_factor, + size, + is_space_pressed: false, + } + } + + fn update_hidpi_and_resize(&mut self, new_hidpi_factor: f64) { + self.hidpi_factor = new_hidpi_factor; + self.resize(self.size); + } + + fn resize(&mut self, new_size: winit::dpi::LogicalSize) { + let physical_size = new_size.to_physical(self.hidpi_factor); + self.size = new_size; + self.sc_desc.width = physical_size.width.round() as u32; + self.sc_desc.height = physical_size.height.round() as u32; + self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); + } + + fn input(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: KeyboardInput { + state, + virtual_keycode: Some(VirtualKeyCode::Space), + .. + }, + .. + } => { + self.is_space_pressed = *state == ElementState::Pressed; + true + } + _ => false, + } + } + + fn update(&mut self) { + + } + + fn render(&mut self) { + let frame = self.swap_chain.get_next_texture(); + + let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + todo: 0, + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + } + ], + depth_stencil_attachment: None, + }); + + let texture = if self.is_space_pressed { + &self.cartoon_texture + } else { + &self.diffuse_texture + }; + + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &texture.bind_group, &[]); + render_pass.set_vertex_buffers(0, &[(&self.vertex_buffer, 0)]); + render_pass.set_index_buffer(&self.index_buffer, 0); + render_pass.draw_indexed(0..self.num_indices, 0, 0..1); + } + + self.queue.submit(&[ + encoder.finish() + ]); + } +} + +fn main() { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .build(&event_loop) + .unwrap(); + + let mut state = State::new(&window); + + event_loop.run(move |event, _, control_flow| { + match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => if state.input(event) { + *control_flow = ControlFlow::Wait; + } else { + match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input, + .. + } => { + match input { + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + } + WindowEvent::Resized(logical_size) => { + state.resize(*logical_size); + *control_flow = ControlFlow::Wait; + } + WindowEvent::HiDpiFactorChanged(new_hidpi_factor) => { + state.update_hidpi_and_resize(*new_hidpi_factor); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + } + Event::EventsCleared => { + state.update(); + state.render(); + *control_flow = ControlFlow::Wait; + } + _ => *control_flow = ControlFlow::Wait, + } + }); +} \ No newline at end of file diff --git a/code/beginner/tutorial5-textures/happy-tree-cartoon.png b/code/beginner/tutorial5-textures/happy-tree-cartoon.png new file mode 100644 index 0000000000000000000000000000000000000000..ba8bb95dfe7464043c7ef5b2cc4af08df098e20c GIT binary patch literal 59874 zcmXtfWmr_t`}gkBy&#=SNv9x4hqNFe-O?f5xwMkXmy+(1md*tf5Tv_XVu7WX-iP1+ zx}F#3nmP00%$z$vcgz<}4J86xYFq#SK%k=hRvQ2SK2L!FY|Q7u#G~By`6bdpUS3m0 zULNA+;cDyPYy$wWe+&L5soXC^``cVcVNw+tZClpftX-pBBl z1S+v{MH@zLr&jfc8(URBB7Q)yL!hm#VjS(tUr=heJRc=QPX;>zlRA8%ntZQD4=J&P zR$uFUY0dak6Kh(#uYiq)E&uhq&0iI5lJ0FdJ+OQ2w_h5nOiid~8h$9lBE6 z(H_zTF%N<`Cmwxnq+h`K4O zD>OphqETiME`>O8K^(8Y8hr`@fG#}(JoevJKeGk%qngs&=P?<8;+zw8exBjDDI0nM z0C+F{_W%Kz*_6+VSY9gX3Rv5C_~b9qyQpWEo|mY+6b!uNT|a%Yaq$Akd)Qcd*;qq- z9lY!zN-FA_`eB3=000D_@>ceP-_lXn?N63PZ~E(1*7-_(#Fd~Vmu)l*O!Eb*5YA7b zNlr&7PTEM>Xt7xMZW`CnTA9%(G&1C(7DV@&Lq~SfgTJqsN6S)`g(u5;DAKtP(h7_K zVnfkw`CldEo*>P5+ScI=W0K!RehU6LysDBk6)9{t@6u0AP4yqY&Ou}|YlQYLEz%wv z@r2#tD9)dgh#F3er&GvG1LkXx=AQ0^*}2~1)g|-18ez_8#XnD6;llhEd9~06^eswd zZOHgO(jD}H6&!gfu{?oNQI%%n=n_#0V=6-pj(yKl!}g>2_X!?cKUN4`XYvP-{Za4+|Z7YIk|#mQBUuJNK_8%C2Hh)G4PB7x?_8p~rEw zpSMmPRpjQwYuOlLq?wJ;{#1pUrBhr6KirSbTf3|7i8k>)AYQWOET8&4wE)!SI>!HAnt?-(kDXR#?>gw8F$2A^81Qir>?{>5jod;)lkr5)z z5a=iMbaf`ZjxrqoCu0b(ocYwS8U0bV!l|9oeFf*z4MP?coyz)2icxvt5pS$7?^7qi zF-}7$VK@J+NDf~{lh;~M?;~OdFF%x+2U?058!lxIe}Z2)-xuERp#xOC_xL5&3&JO5 zQ30y6k1I1?!G`R?z}+mPoyR?^K6Cw?3BvKVp@Ubpo?_Pl3VRG}GHH|R|JZWQlc!AQ z3G(|-vTv3e@(F62QX2m>o}9#le!9K#o<;F$PlXuP?q8{>)=mN+^TzJ_VD}b3OYhT* zFM~6q&Bo`GQmMF#Zwot6PYtsj4$xuXZjgQ^a;gCl;0fpxZVsL=6P zTx+54PaekB3p2B+Xi>vUX|MHjo1j9k(4dy1mJP#oaa)In2C3R>G(Fcx=p(|GcwG(M7y?@Ek{ z7Rl4TlthQ7zNleeYo_eBIqk7B^yxcVM+&e8&hB!_lnmBZ^qh1nf~4}M;R6!Y5=9ru z=ZAY4pZ4kR*Qbq^l(bev+@8F%MhB}~T(fTi4LgiOf=b44-+vy{S!z02#V9Nfq(dZ? z7Hr=&)OFIbL$6{ZRbyTLGg1S2_;jHZOYYcSmCh2nf0|xw9-v$P>#vw*#Ie-aA1Brv zq*fGxQ$X_|@iO0Ek;2_?Iy$SVX8*29btIr8Xr5XN@NiLJ*{7+Z?vE}1`|mJh-2T6J zt5{A40f`(XqnT?XdfXcOOG4`FMH^jK?|c~sNey&>oC|t4sU6*_$z%*`L@3YFYpdKz zchRbg{y#Y_L)Y>KsCFYfQYvuS@_g{oogK}cMiqYpL5{EKq*GSuJuh5Wy7;V%&YV8D zWUQ8fy07$r0p27oGk%cMew$9Q+!MF;gyBj(HD-jAj*Loj{mf@k6)+G44w1xj$@V&X z<$4(`k|5kesEBSHk5@=nKc|x=t#VIpj+Gl|(;{8uOW5rJeo;zX9Di%jkI*DvMRK3> zCNi|6tSDjpA1Ga>3Z_;{oDF3bwa=O?kNR3BR43OB94LPVOOQDmU$dW>qh=e#$$MU7 zA7tW7f=~wTQyk^;*P4OiZ&7z=S08~%(`R)fJvB$Bzf@wj|Gvlm!4Z3MWx7-=}+DW@u4sZt^k^FbfKAXg)L0$|v&vxNP^B02vD}Nlf#;uMr zer+&IaK*>OEaP6{rIH3gHH>Wkv60fsyND1&u?HU>uRXGTt+KyLSOqZ2XLM`3QC-;~q zgPsQOGe%}Yt`R`Me!YlEwr}{eN`m;&NrRk-qZjPC{6dL4LnXJT;j1-1*YjQ!RxC<8cs-iBdMCXq8y zY;&ovn`1R@M&&ydlGd3_mC{5ja6jlSezM23T{JpJ$;W*!Mu#M}!4-fXYA2<{3@>@W zu5*<~Tn#K34fE~%(^w_J&Nh^&d(~LkJ%Qf6y@A%?#|4 zh`NR(pu{WMTh5V4*T$rbi(szomVeCiRy2vGSA~E|YV3%UCU1oyVVN}sJUvohJ@0gk zkDJKecF~gt0-cNr&Hd#g*q`RGk0N5Re{|xu#+Yqji%XySJ^f_|Ge-}EpW2eB>e){M z`8pbuxcTH;R*6D?s_!M}+N{44?V0yqdTP6wKilfmDU?Tq?<%lJ+f{29m%Pav!{YW> zdguT8sU+UL&9$EAYfN}}75m(elUEO(V1QX*LidmX{o;I0wE`v7RT6#>GARW6k$0%J zv>m#BE!?rdV008uca0tTp7=phEp4HlfJ>3?%WB>icYaTy2g(&T!j6Sh5n+h;Uf8Vg zYD9&JSME2^g578|`Thj{x7z#xbRYjk9CW-^?O=fS;ax&l*~1(6hJ=$>V;g~dv>KGt zrV;m#HS%N7yjgpKE6F?Fr>kpe*QVkN!|W+UnUua@YcJ@i=WA14p3*ISasfpTw1L!MSREHrMyZLutJ z>4@J3ww|KFj!_yLsiEwr!)54)(VZ`Ru8uxaKnA>KA_up&aY`AvM1#hiHRiwZB+mq^ zkqthQGIF`Dy>ORvk(ACm{}&DFoew$;?hhP`g(8GekJhq%U^Y@cj7^_w>ybDQBFujS zCXUn2XtCOpzDW6WQAW$S!fxN~&|1cqf=#a|m2LT=60&+UYCmxqGo0<+3#71_8#YQn zXOp}4x2hQQHA*@NCyjJOUSYSG3i;?4uY|@k^8Y>y(eXWMso-+Lm-NBeeHJ*<^tf-G zCy8F0r?(oLL;y;_=o4Zo05h9-;Tsv{ELeQZ{qOl%7rlB;fSxTjVPaRvQ2IhsQmG)% zQ}_8#Dup$i1v7{X^vnW!@y;L9IGb=B&7L|&#ec)~+qAF+cM11?TZ-3->NKMGs0J%W znUMlWQBzZik){u14zeNISO1fc6!{LiBXeD-dkl|t9n^rphGHc1= z0a>BmU!dgLzi@DAwo2=g;4gW%ZlJ9vj_&l1SP0W^mmx7|?ms{c+UJ!lT!0z?2{55A z$KUzv(ZF2am$K}1PoLD&8^gzIGx+hITP8~sn`17a@IaEE z_2HxWKf64Vbz<#fK{qLf*d7XFySxfqXPV^^yrt=PaGMA+^zC`qShLYs%cJZ?af>9G zdlxgj5R<8_n7NE??w3I~0?bbJ0%57iE-&$Vh@Rqs98c@PEq}%_Zy+C>{D0jju5&!R z0yMoC=nc5iDMUN4Mr9ZuC=g}PC^+=c!L|Y9fCY~^fCs>-40{Z?f>D=A_@Wus4>aTN z#MFy--501!sTD56SPZyNb5*?w+%TE&$Fd5C_oQtb!+C8Fwj4ux#xZTg`0yr4sT0v6 zz2Qrr=_H6ls*eNxjF6H7(#Y}LFw|p|n?>ah{dzRlxt51`_qv$=HBViV_5KCk;6HtT z1^*6z9J1Z@v>*y{7n8%FFOs`s^LAfz5bZg&ZDK*6q3pv5f<>P}Imo{!)!bFEgmk@E zpbux3Yi)G`b>(^e&I7o;kU3g8V}&k{QNNGGG(z~kN}#KMTF2a=y6dz~gJ6TO87<;v zb#>WCNM4G-25_XgA_Av}E$}6rCDFu*Iph76*AUUXwLNd%(2y!zQacS%UTWuyOC(i# zK}@Gm+&vQ&+{$MerOS=oQ%KV#MwA8V(eGc~b(Bh~Y^1yFPF@+T9&<~zdz8l9Y$5KG znwX`XH0!POBxygBt4zXHmzcLr#SBT_Kg2fO`|ZW-kiSx@pJ~7M_={#y$F%JAn=l)V zF3vYPE5?+%o1HeoK@w7d@3KZYm4prZ382StJFKdAPrCuZ=m6`q6cRqYzFMB}O4kZ^ zG#J*<-T3>x&)bwnPRbmX)e6%>_L-Qvx&@V-qzb_Qs34wuZUFDFwAq9|5Bpe zv`;#9^Ua3>{FHn`WgIt2nJjOCVA!fYp^k?KHZM$&8Bo=ExSaM~To>OJM_iF|;P(rQA8;IQ4LE4+bQ zh00mN#%-LV;v8VJ(HEKPyYb1IvH2{leGT&>a|zAm_mf|Mt22+!;Ec|Z{5mH&23`K% zBOPuZA#6>L(F7<>saAWfY>gH4URu{|$8<<|=Lpfa7o7`5Ev;Qqe=8cL2G66|oPvFW z_hXORWv%QVYG_2gHg9(l3dW^KBUf4!U7t9h$ZxEKW1zpbKlLb8EWeVCUC=IaGXJ>e z0r0&E%UY1>z7mi>MPNdSfLkhIKY#wz+r%e>qAV2nKVT|o!EBq8C{{d&E8iToU%EvE zlb_&%>f*}k)CA%~))L3fYq8BKFTf9;8R(Wof17Xi1pgf$KWYSa4@w~)ALu#uIM(9$ zVA+ZcxDPe)#)ytOtd0NZ7j(xl6T6wc?Uv-aNL8|AQn7l`z zw%9>6DH|J`aUMq`)SEB^)gxf5M&m;m|(xc3UJ0mv10PH3qiZmUd6G0*pKEm$VN)`6hoefwyfM!wo&|`<)p7 zUA!q|mjk1J)xIW$rjV#|*jE#y1JC&fIn96-cnoG9G6l%n7E~0hn&Ax4=ZbbJjJ$IK zXhZP(^-iO})mM9Y)lHH~&@-SDPl|Tk920i9230XxLI&q(T ztTuWVzI@04KN>}8{YdyEORh7xGT3}+_Nvjs|DU6J_!+6G&U@_8of7krv#N3Xe>NK` zces`_|1A}<2QMzn{;6qZCaRM#>Zw7G7wT$c+srjZR=N12Dl`161eDf&=u!gqrmjkd_ z68y^RX4vspe+TZsR`)!)VVrvf*C~z>Md)ns{778Q&>tlJz{mTMxWpytPO12grr3X% zORc+O7d3N!l}g4s$S+LtnPpAIpKc^gYLA_|nAXWpOAF@RE8RUVj^gy2)UJ4-Lh8m8 z*-@eP35FAq;OhJaCDU8?%V%vOmGh|iTPTwj_sfU$S13Rt62P90YrsGx5})$ygU z9CF0HZ4f-TfA>25-1`BK5u>WU7@c#R#)2<4)b@AXn-y_NPK=7?p0d^dR!{pk2mIDr z*8bT5wgGMCoF~S?d4zw-vUo5zeEBT{m?Av7X~w1RU8F@s>IhQ_MOU8 z;*I^TzzewNvwg|ELZ*(bO}Kx}rwTytzsMebxnsCyBVy)5|Ce%HR^7yD*>qh$^xxIs zoUs>NGWdm&SMb{jV|?rn6$>wh*@o`>c_2Au*!^|7`BoNAKS&J(>X=TuDvnMOi#k=z z1!3m&t3sD_gVe`~QNMI@(aY8rb@nbli50CS)+=3fcLIJbO4nb50b^{|@w#t*tMm{T z)%pivy!s2qU-nTys|}>FWOD7;1xYE@_}%Tl#@_2d>Bx!TLD57}7 zmI@S$s`JhxI=xn0MJ0j!FqLb|l&2eYpR6o(r-h2vQaIG5O-@sLF+R&TSV>+Z#s69; zw>xGYm-2>`c7f$4gnK)o46R%8z6<7IN|;r%u87r`O}y5_i*Mj{XE% z@!!EAyC31dqKtXA2jlAX_0blMe!Q8H3sin>{O|N-Nr(+6v4R%SXS$2)n*f5;zKN`c zHr8>-h0MHKI%f^x2-Iynx}d=6F!5|CJCldVf`GK z`slJ?NTSt@H^H{WA_RL$3E_D*U^pI;5_Z2hD0ovp=akaUQt-$baJi=+%*xMd!flZx z9-?rR0_?a*&;gZD7~fl$(qTXR$n(gnRf-ID+m5N84uHo`^+;j%Kg4m=y6W5SM$}Z{ zXBHM9nmy*Xvv^rnin5O0I%WmhIj-jMz*6=a@~@8);D@9iOf8d=#&6%&)W1XXogoKJ z^c#7WgndufpnNg4nEf$G50pKRrOaGyx;Hhw=woD6l{Hell;O|YBCUiiO^WJ_Z zWxO38aZ@>#jiWBkrU#XbBL=O518TZ!bXbc zI72po`102lVl=f=v0Ep_TYkn|$IQizO%FQgNjg|}!HZ$iNgxkK1PyeV)gGUPU^?cZ zopDI?;qRm;c)v4JM26}eN7yE_yrTP2`rS#pK*|)!$DL9M4f6M5E0iUZ66 z^u2AKd5Cy+V4jC0Y0$E-P2hc>e<-K6u6@%kN!KtTRcGq8COq*P{UwKLs;cnfa*4Qg)7VbLc64Guc9aFJ*YVYT$_Tjcf zORC`Cj+#OkhShvNbMR|@4En%CfS8p`<3I5e6nw{Y)bU@N`4Nvbu9-NmOXvMw};0!R!bL zFS@LzJ^S1WO;b@TZ5IRd8J>C?@O3XUnEJ-f5xPTWa}X++9;Ul;R-k)@;gz23zZ$zI zB@rP|BJAbc#!bZvrC4E*Ld!r-C5gBMJzMw6RVFTqGWV>_2_sjIu8P+7GVCeb-&M~T zkg?qdv@w3SIdq&y+@*a~pZUm>&GNNZl#IYtR>ar9f}z3KvB~_7eZk+ozX#qqeQBAwXk8ZIC&0 zTo8Ot$?g7{Vf%JmdfoHUeae!nW2(}MLwD@mjf)p$^g<*}N*LP9o;j>(m%vR}M$E!8 zSD9A`r`~^yc|W$KJP~`qgApUw#PKrtFyHgr~9=`IP1>(z%Q3{Z*SHCHv9wZJMW?eFw?JC3y_$wIG9IUWD^Wa`aw46>>q^+F#F0dwc|9gzA6%- zc>hkVg!Hvz#5>y@vfxi&=_oBgMfdxdRNo@k^(*aZjKb&f{&syI-T>POU!wQ%v&;ugf-^AA zD{X&L%{lcEp3?W}hAY(RzIV5`7gqRm{gYG<+c`3w+V2zfi;c(GWiH+)X`#!G!!BAp zw54r4zBMAKv9zW5#}^W2ajTmcpWV0rNRliLX2o{|rn;TAd8kEjiy5FDmc_ul&9g89Kyr;6Dr?{37rdc zZZELM8c_r@zm`D{+Wct_i|K~<-}Es^5lLW{|0KNXVStN~>%2FdCKyPC9f!U=U0@S; zJHJK{l&{;#WJDUDp=g4oqfdHLk{=w%mxz3Y?BJ6XN?qjj9h5(6S`v=x>`JA}XNaaB zb7R$~H$DEY9@rQ?y43XZGKU0ORzQ^#%I*Sw0tS}){A!l1=hJim@7uDUsK9^2>3_Ps z8zTBLvc}HkR$dZpJWo&C-(tH$%IcwhT$1uEn;9z`x8ssT_K;5EW_%@$d(+wW6DIxy z)1L{eqMS@(4O42Sx}1Q+rC;}OJ%&8d;`8$>O5pmweKk(p*6C#?^UN&=FJFQ{6;q@5 z$+khDPxHf1!#z5FfL}=Mx=IFKX#Bw;C;$Q(ytT6FR9Tv5TB(@fJH=s+m3^2`z?HnK z87@DSJ~nxxff|*+UZW)uE7e@(+FdiRWm;e~L1Z+c4W5qwmkMw_gkWoyki`pVsxsos zM_T%^8eQp6 zA;;RM{dLi`?}g$tO}NS*@Asg~QTg;F=Ic@ebDc4gl#2__${~m8+n)35o6tN)TUbE{ zeVmA>{t(-MsX=h^1o+#fB&HK4dUAo(jL0*%Ty=ei8=|@K*1mQz*^NgPn9z)_Y)*x;`hZjJfpC{GD-&8XsOsU$U2dad zz^4ixA?=l7h0z#0bZB*dxYVxqYdso5I#Cu9vaS{FZq+mhF6;QzJ2@4#8HLFtk9Ji z*X$nNPd5G;%6mm|j;`bG1s;@5WFzsL%}^3-7xXTYB}-~V$jY5>iue1nr;4U~rm+@PTrc!DbaiL-%n3(o%TB-i@Q|(v z81*5OxlO99RA#i^U+M_On<=)jPS8`(07Qk*Na62zRJ9ESV0@-Q%^1;}h@z9Xl2kZ| zH6%)3JCPL7`f(Taq41$d(7D#)Yk0QDdSV~ynmw%LQ0zTNctv;=@`nBY_X05WJOp6B z>ptk~+3qYGjwz##e-2x0r!NCvhd8~(iEnaez-Rt*5rP*QVw(HyVf3F%p`Lp6bTNy9 zz#FO|ByMT0l~N$D!Ei)kc*y&2r`uW^mq%WLJ{=7kJt`u1<>+hFtm38E1OHfWbTg!I zjz+gaOmuPH$>{TJmDf&s#mNF&B!GtjYkd z;e`AX0W`B%?R&Z^hG=g#tcmVh)susA(Y+mm@eQ(hj5ZktT(F87l?p;fQejyfAMQJW zs69rvTRp=kd9wKrfk?}8m?X_S+`A0D`>-xtQq^MpJmGZh&d#gMA;zOoBmtYDt)KyRNx)uV^YRv7DNxm{D2J6-G#`TgW1E*Sr!+gD_ zz1a9U9uV@znhl~|(wow_Ps=R@>%;IK#KoZfFIS}5VhMWb9WMSYSru5%BT75vZP*i2 zK!io0(EhjmKm%tAh2;!WF?fSta#J{^Bhh3Tl?%r#aoUtU8WAva1c?u!CquG6fVS8l zTq8(C8`mC3?;hmGj&rQpOAQLF8m|gK6GqJV^1s~UpA(E5Ml$$=$m=BUd%IuuWi|Ds ze-xy~C@&@b4b$nwhLkp+qSO`G3+o=Z5=a+V>R1A{Q1hYmw{yUel+%Y~jHV%t8;&n*|lX^)ybWSFlh{NcM5@}|)`sM4;8 zh>6v-#pF{Fv+0QyM#MvWIef5oC7vTy89 ziKIqWkUZ{n?H=B2FIp-{Ds4*FsAL}iLD7hqYB6EYk3ND$4L3+AwAYf|;=YeByH0JF z?1msSex~N^2O)qe8k+19@LrY(E*6iw*@hyoBfyb}L8~HF}pCpt$%U98c??{cMg667M|5N7f>>cNi_FpwH@_qWuvB5M zH59wP?MRtnX}%tRy>swtzm^nY2rDTze;cL)K#N283LkJpZ`w1ONxT!l8g6hNv?KUK zJm_sgl)4q(+CcKT5c}XSN0^e*+WsFannEH8FUvpEMDllSf5Vv2Se)WCr~Ai2?29&0{mI&>U>IL*7VSm(57BAmBe%tBa+M$@ESuBJxUlP6vA%1YH1jet%V2Mn z(0Mz%%kcU?QWCh_>PnK|tJ#b|<6^89+;xrf?g%@C7P}j6%1;s-ljf%7{({VBnhgrO zV1K8WDBZ$hyy#vVtk&PyLOK&f0`q^S7Cm6*l(OS-l3g~%T@ea8o3Kj=hI`l*a@X0P zZ2bv6C0=Rs#HxMx!zB|&PgvJtbCHP2{QO0PZP;GkBgs~KO~nt*xzHppRoNqZzLRBr z*0887R(8X{w~W6r)fI5_hbEXG8!y+t8T1pHfKM3{V``q&dUu%?4U75sx$0l#`$R!*el2kWa z=><2DR*&s9#+!V+xT+OBk!C;Y>_Uf|Gia6_ZP6SRazYzl{d;$tu3_8pM~1vrv=-px z?m-P|m}Vz0<_WNhYDN3{LzJ5AiK?9BlpN{=m6|=SOA;w^y?-V|1ouNoo=lJBzU@!h zV6Fd7L+QF0D*!kxlz?3+ObX1LvKl(PiEvUm(`kDU55Pnm&uEd?V;Dfn%R|}bAtwM1 zG1z!N7>U9G+udS9bs*nak1fWFP8sI4ZU&?BFnZW=tqr4{Q-Kuuq<3)(nGC0Ec@4qy z3});Ay926@h%$VW&#tUd$9HQv!c&X*DspnUzb>QMm*jZvwtELMLnUxHjXQDnqZBhs zyNJ#2{|(bcp<3w`Th_7e=HSAHkAV=-s@j6;eHWr^KpY*Z_%#B3Rftz@g9~f34~Vlb zRh|b`d40k5Wnzt-n|2EETfvS?)+)9n_2T9 zMm++U2iZngYQSu6QgrD42c2IVvrqe$N*jfB{OSoZ@U#c37?)^DEr2?*(DXm+q`nXn zq7H4!0Dz5Qq1lU0)D4kjVj%K_5miL%M{;vL_$e#uMT2xq8S9#W`5^x%a}9MOr>>jP z;d~cr`)zC)qiK{jz@1Vi#!E)P140Otx@qC=zDZg&pUYpfA#27BPHcPH4!B_n?qNEW z2av*a*3U?jFI+8%fm?wFKA4*=3u#I&XC*?!!SM%*QS;sji`i8{2meAcsWV1dBENne z&>YL>riKAjaaH+5sq5AOze2I`Z-4~l?v`tN_Xy9ZM0o33&UxsSBx(!M_4)KW!g)7N zMJfd6DTDj&mt=KEDT5Zi!8Bx?dv1h8K`;NWik6!~?To4n2auKZ-6x~SuNXlKPa~nu z#F1vqto&8?(k%31HQNCq)$(^{cU>qja0oh%+S)to(?>TG!V?mn-LsJCMZS5e?!>J% zOu}1*gRmPFGqK-VVsj`n-k5dGBrAhd0Sab~ed{m0;^(RG0PD9c+}<2vXuM+_w4#kC zD9!YP)MeAIGXW8W%+J8`l&JOd@ucGOPbv8Ngu<1zL_r+=F@UgdgAC=j#I8c z%Ym(CCQFJ5uB#J%T%Dme$}ffmr8x_O$NIz0>-F_i_{&uvzEP^zObZ8TQlS8U3>EWDeoGcALj*JRU(1H#iUFJVw9&&!t z2h$f8N$^&BOja1tH6Y*>AQ$xQu=6S^ugojEaeA%Is{ks1=GrB^Pi`~?&S#(a3<7dk zUWtJ%o7y~`u_$rKbNdW;G|5Uh!f9&G2 zm3b<&vQ`#C;#>*dt+uK#rD!@=;x;=#Ea(E1KSx0EWU`e2D7-baI&^1SEm8#{JW-Zc zZkHs9a7$c_C1)t1(R&$Q7P1-1xvr=8AzHwJ%dF6VXY7|G=%QW=TjiFIME1aBP(|qubsN@Dm6QgriHbndE zLUqkxnV2fs;FlH&vgK%$)1mc)+%9_hA&VIl(TrwMy)GI6bJ@7 zb-6@k$3=)feVq-r<#CcP zvMyTEg|L?ZfaVa!Ka(-89jOIy2!m)&s1|Uj0SF?Sut5aI^XwP2f;*0qjF70Gh_sIn zqmBeT7-M7P*eP$nQ;^5>ZkEO_di zVMX+!HNIV!$gYh!dBUN%ldc(RqFdjLY?~qiI~dT3ziLXjFUl>$vnMqyh zGBHDzLi$x(rUCDptDs44o;+^cZ4l-rcR(ryw8?%L_s7Vy9NVuF2Ds&ZGa+GQC%?^Q zF!hzkp9h%jlZFK^e)P19xVZ3qhNRYp&BCHy?{H}4!?y$Kc0)VgV58Rcu&J3QQ*Bft zWX?H=V1=(X_EBT6C$zQgYlRCTGUdX>08j}e3Q7XyzMut>tB^3Nf8^HqcLD)vQ9=^s zXKcPeX0RLONHOdyD<2`b^h{N(k;(l))>BF8_9I(#(9G10KOj^!7S{Q(uD!#t3;2>y zog<8y*Ubc>2pX}SuwhiuG}0o0+=WSA}^WEt)C-2c};Jagr@1B1RFU@_&Q?RLkyeW^sMC|LuWx!hp3xoKUS?L!eDH-s;!BO6)T5_$Udx+@(?{RvBilUaO% zLENw51c#*sAvra>zJ@6i-n!cJX1+2UUx4^b=LoKOOifiVybeDFBPd1<9<#o z+TaJi9)f{L;?4}job+VqRWlEE;G%IOJ>L!}k3{LR-TZyJf4#9SUB|M` zr82MJnFhAO3z{sN&8(F<3X?ZM-2E;cRglxRW>%3%W|EjPq&K$c(` zO*M!;tCqfSW0;r|^_PMEy%{NIsrU7rt+2(WSJ;eNI~awchXa^bYyG!>3EAof zHc$d0lbs8IJT&**S}Vi@^x3ET#+~l@Ctd|4JV+o`Ldc)W!Vr(Y-UxVN-2L|H3s9th zI#^VT)DnS)qyCu;S-nHsq?90GG1k?UvxO6&LzzYxs}yuocz*+`(z(jqkMwf2I!p>3 zs7Vc@*$aK-`AbJI0g}mJhCw0W5M-2-eMkjR3Qv_R7ofGmz%W4n%bs<%USp$`-fh#o zT5kxk8+nQHH^1pYU?F7H1enyaJrGmDs>5lg%oxC%usNPrntwo%@s0~SWjzxW+ zfxKR~#&Lx>Y_P-o)|bFJ-G8`z#e3g#K~Mp*;$mtbfx(WGY7d6if>F@)htEajO#_ys z`G4`=YE8VxA+AgDsUtTUnR`TNylZ6ZcYvYpeOcriNJ^^)kK+!~E@fs|p zJ6|r$P~3^ES&n?*@4yhs!$ze}87lL0*mJoKrlBQ>E|p*OsFgVgecQD0T?v z9wGisT-DSn(F6TJeeoZaF1_j=eVwMhZd7Rvv15Yh7l~6~`MU%rrzOV{me_K1q9i4em1s^&9=cCjE27muKa8a;bP{kHT5|UOnDANA-j?F!CGyu(tr!g6O z8=$T$ufXVKN^()djf*b(xs($Ku%s;DjMnJtr^jNv0+Ft_RLaA zdi{3oo9+4BjKVxdwX=U?5vXkNP@GfAEYa*9^7!Q*c@;4IWXh!gkpwtZjbIz>w{v0( zX#$Jul(!aypYP+TphE&E(8Z*n4)Jk1V(Gt{uYmEwMi~QJzE8V6TS922^V7yM0clL? zI+2zOa2nCwe+;Im?SQW-;pY4Oj54|(prGVXk>0@dd$Se4%@)Rzef3-C$-w-1RBa8H znBKGu1acT+*e67=#S#{!@O`&@m_%JY+7$bX4A(Dr>}hb1qJv4ErR zFBpPAh`?Jq0MUteQR zaOw+tOPC$0z<7Y_#K@@7Av-hJS23fZ0_H0h5mO@HZEir9oYa zU<~a3a!(c7jc?WS%66|Z+QViX@kp8mygbKh!*HSG_EGDu4)+W{6)EB#wB;jW9+ zTo~2Dg`#rxoOKU>N%Bxv<7grb7`cF_sl^|JntMAr`4y5!hH-@E|DC*yLm|uCWq(b<{kpX+c-KReNOj-Lc_e$Aj{UD!@E@($8MFD6@a99{g0{UqP0&Xit>(-cN)P9;MeDXS6sha@cdF80@$&e~w z?cT9%S6FdDw*#;`?yUO8qcxloL=y$_#{bZSfM6}(O!5JzOsB>@XQHEJG&4M~*S$-?*6B}q1) zJ&`A9)H|jX{6iOZi^iP}Fop@;%=1Yp=&FkVvq55$l9dQI4VbLz#sc+GE4?Obev={? zU3r?6E$+qeXGxrmt}z)9OmYKt*ThyHW~Wwo;U=6fJixtUXNOk^3=~}O!MF9lTnLjO ztXr`D%3o7mCtMyE4nudcFr5V(p9lSv?fz3vmm$n!AUlflZW;HTfg|;KR-QlZjcM7c z%U6nx|ANh2k3v6y2CwqjfdEN9Xc;OyH&<>gWd}zBzJm_XgQ0F~bM2Rp&fbmHP?Ra? z5M$*pIGa%LYU6U}^%+^io1Bha>pENC*8dUv4T0WDl!O3e!4pSr99sHhkr-S2#fdWH z*ehV5mFSu;e-sq{b}I}rw;>|+=%qjYK=~WBDY2q@+qLHX3nLLO3&42XWRNfY0S0lk z$t7gkI_%`XY*&L(qOsD+jL$aC1Y_wM7S^beD##{C_k+pv189+p>N-|kChZg7I|6)X z9wbKcn+nP0`*sY)ek#;*kIEIF7+J=n-M+q`go(w(3v(An+)xsv2~t^ zTt<@Wvx`O7oLDb9!np~&X=r<{f8wC@_mQYObEzoQevG-}kEizQ8*2$jxtjZ0M=cu99?M)yCE`$9gfUcjQ8}Nt`#9pU{6KVNby)!Jwa5%I2_R2?-WQ7C7bjEQGlO^38pxlRIhQXTl z^f}S9ulO1;Rk62D*kvUEyndf4LuQ{*#AxPWf&+Q| z2%t6B>dDIuFoWu0(PW;c%4rsufzFJ8@SOnHWN5bzssD@PuolHn_kax~P(=|X`68Qx z;V{JLcx8Lxu!$4ck-&#+2tYH0T#a2ZY$DX$v3OIx$jwcbFcT|=g{MspXV5CR21{_CF8y?jkz397?vuizr3 zQKe!pA&zdphp3%~P&&@&D!}sHOZvvnTYf|3DJ%{ope6kv+hTBKxg42Z@h<5S>aj3i z;2A$D*~noqS6DrwB|Z!L+KD$=jp`48QP0|as_>c7CyCM6O;s`)*FF3#>${DsD0FDeL6$iwCnvq=j zB_IHJrgYrU2Y;H|q*B^YH#Tio{qV{6)@BURps{ z2Y9OzzNC(utmpFrIx&m(u+P@BA|*1nr7wLE;^-Z874yt!pHoW~o&Mzm77SrOH<#M|J54u1_zvL}P zCkTk9A4d_CR#;wMVtH|iFbpYVb``%#5}~eYEEaRuiGZc%%R+quDj zh4o{8$567uLV6H|5CZ?n$G;4GybBTj`DZ^s?T@t+c=iSwbd5rXnov2Va0^KPQi|)> zuj9o(e-W$Is)r#Qfx+W?`z3$8H{dQsFnnO(=?lwL`)=Tl5S$0FP)Z_-2n1{z1EnNQ z^sZ{m(;1Qa!T=V;9u)3%f$IvSwG=Q89H{H6%^zAJ5CyJt2ot-zXK89 zUS0wjP?jZ{ra5Sq;L!^>u6L5~`-C`tG)+U^aP13_rRqP=%Yd*BpSK9LYDa&yR9&x0 zcU@IonZ7F*R#LjYf{q8E1hC0+#Bqq_`8_D5kqXWH ztt2U{U0GuW+EL8w7zuRTK!L z5K2lHTXcg%sb~Q-tPoz@;=iPwG|U={07z~%*2{Uc?u^B+R38TZ7&KHQz(K9VkL$SJ zO5q<0{gl=BrvqnMhH6;>_dtU}?hX_H1MSY3TGtU60M75yB%!nG*T`ECqOBb$vyA!$ z1_8929|#a5cfDGpX&S-+xT8;+#xy+87(fc@4anBo0#E|h06Gi^Wt=6b>yC8L#`nh1 z(1Mo4BcK>NV7Ygpfkl^A6t1dJSh0n!8e@Rpch6G*F}9kE>{>_Va9nSu@b9wreyq=e zUzSwMpKsitkaGavLRU)f@7=bEhXdSY#|TuCYH}nj^1MWx#FVNBFqp5kC5kK~mI|Kq8C? z7qE`EtfcI!kfb0b;J!g!6YZD<-H#F|eP|)A=)S16G(-^~Qqip6T(pM)9OeJG-Ui_x zBKkhS$AVuJ1*)uSJN~pT|F@j&;||&V3A*M9nx5X zrYeb%XRRX?H7#)nmTC>Gn^6YWq6q9&z>$a8FK~0wSTPuDi4Sn*6hMkz{(m0_;OPDz z*BdST{CrH{+0C!(x^?xD1K6N*_g_QDjGY5`Kk%PD{U=?zT)2J$cF=Llg~)lHK}u>h z&eKD*6tI#EYo6t(suF>wUIS7q)MbgPrqI18O2AmE28_Z0S}8{evTh)#LS5G|P2Kf2 zcT+~JYaOOsDxt0`8cA$SJIny4LE|_A)>;plbVCfh6d-*$(^mojU z1%I0(Yk_w%zr-DAng(GIKq%tTC;_N2Y{&Qs>o_`&<{!Ih;QI7*P6#1rBp93^3kYQE z4T^%;cC&eks;CgoXPxt54YU-{VE_#3mGh#64m4J)HMG_^S)9OHi}h;NRwDWH&4oWt zu)WX*zx0P61^&!wNjF;{j^j6Vs?eCLcT_G{yJqi}>$MB*XNzB!C5`>gGL)OLOXaOR z!M{M4-w(;+|MmC%F@@V(*Q(F65nb>PYZ^!T2?%d97RK;^UPG9g1&#aTzJr#Oh!g^0 z5KwiY!zi>?3b_La7+YK~Nk~slW{?7ypUfeIK&W+_irB0+q{QZFFCO8aXK9ShCWmQi zq>FjW7^Dz@j#1<*7#*pv>?N~9DsIA!odpDas02|oD zmE||4q0Zhe4ACyMXFyW47@dNEKvmb21xS(z-9$2qB}`RAhZ?7gd3!QdRa1*_Sj^{X zd!kiQqHY?*Nlc@~k{EzMn#O4A5_MT3NMe*_O|6Lmr*snM=@(w-Ag&Az@)0lwST5IC zoRaa>NL^-O$Z}F^Cns|N7Eu%+f<~U@h@zlX zCRB3RXg>Hb~($J#<}(t?fXP6L&GAe&dk0bK^hkO5iFK><8+49pDN2 z`TQ))01G_%ktcC}evb7w*1$F3zbHP}>*CXTLoMIUv~@6jre7ETLr6qX$Fs>d8^lSB zswfesWIW5fKp0UUPXLHc7f@Q`>}=5%2l0}U=6`Z(QAr660|bFWSx`HoBo3YC77)TB z)EX*KD2vk79BTx@3{~YCd^#;vRyDw!0@0wXDjH|3CCaiQm#L~yE9JTlu`46G9H*rs zhkiKVVkXxp;=eNU?UEgbr*u&|)_dNX`{KYT*DiL&M0w}23 zPWaZ{OS6;E`{WsT^ET-9Wm!{t-q@D)cjEB3zzIJ9%xC0nhurwXf%HnrcC?uFu>|)E z{#mliki-#^$W0DusJ9pZD9Kevx*a(r++YG>4NOgK#M3y!$;ljHs1XDTX&ghiVFjik z*FO#e)K!Tx&pU>pFyM+O;W7f!ea==KZzgJ}@#c&Q+gEJ#Fcu=cp6l^;U#}ip599mj z%EvOmH@@%8v@Fi0&d^GUM{g&t(@m=-RLpXKtez%iU~~Jpel1%)4;g-V*te# zO;w}FGsg)ax}lPS2nAZwyg)-8Or!*LCIt;KKtt__h`MNLf~l99VC-L5)6E>Iy;-i`F&`!ce2kb7bocs=R0oq>>V|S%M%??aIJA!<~fxCD~%$npaZSi!~?PZ1dMZKjB6|?ARKwedE$nq2TMpQD{L={-wMP5e1cek zQqa#F6InNr*e@jVhaYW0e6J7)2nDRA4q-Ed1!p}oKwbqE^7{$DRxn>Cc|0;mb1OMbhAH&~#-)A6PPyUwQA}oZl?Vue- z?^*V&wbb253M5h38p1rwDc>)s8<4TBr9@eljo`i`dc8rCCT)tt7=zW48g$N2=Eyb~ z(pl0fj505vL)Ul|{m!OL1i}z0_0@t)3I6;m-wS*e)GP2A0FMK>0n8V3BuUb%4LFn? zI23-rYKvQqv9{+q1VawO)aSe#6u~~>_bc3L2L=OZt>bVA%)ZG_Z^ieyAI9QTsjpwt zzgn-5-^{r*MIpl2YpUs43wlif8+L^PouX^7Su%fyS0{%!Syx+Z`c^? z!g}GJ8|ZAk!YobP*smFqBnGU-YPrP8$s9=%;qv0rH62tqJv{}4K#}Dz)*?w_8YSk6 z7G|>;#u!{)E}^x?Y?h)dN+`}}3h5Aji#VpCg-un%SPLC!NYZ1}3fXBwDS*HI*uTa9 z_>u3Uh9F-7^-eRu%B;{dO{)a1!VsL|a~DN{s;Xcc3rH$FisKkj6d{VDt$`$y=@2x|ZX%?FGpEf}ec))A&cl_XA(@ zlt2w+b%wgG+ZqACM0i(|P=7NspW9?EmdoAo6;LK6%6W<8ga(qtaopPglS<%S#2g&E zURjo5yA%=Al&q)d4+Ob_O6%jp}if8L%X5%zpA%5_Fzx34(e{sMb?QhkNE2M zy0N_p!lDO~^nWh09O*1Y6a+1ajsvRYnG<6LT1P(Ou^`dcbXzj=gjv5f9hf_7k5?HXw%7vG_q8Z_|_A# zx=-e4s)laX8pP+mB*9nTMT(?;p$3*fR%Y$2grODKwUHh)Y+$PTjop5q|8DLYqQN#m z_#Gi#Q+K_J>&2GP@er4{`g#l}R0}B}#er1v13UcGRZSM(d8gh1x+XVPcmyFTuV1a# zPI$-03DfUpv!wl7w%(wwD+nR5I9=fEbm7Q!-FxTr6xKAT@*JUK_J)CmmI7IpAxS7q z*Hu|SDMj&sl&}r;08}m?X_^{J2Tn;S$2F$Zq1IvR(%8m8NY%#9g!+@Filc~JnzlPI zZG^efsOuV9D`+KYEN_sorhV z46dX^g--a-L7TY(W6ZZu{O>>bpW>Up_D$k#*a|DGwg9v@N-45nE9d%79kXc~aR&05NgB++cutEzzx6kse)9XqfpXy`A8_eo5IzdXyJqkuHB zmDJ=z2#G)oXhkwgx7?_&M6|HI7G={Z65NOiE z0Lr3(X4KgA1|UTtyi!Qp@}(7kzx%=8#ecv20V=?_=N>1Z8taI_7mtRuE&RqQfCA+5 zUwE(iIPhP-`kU=oW!wO5^qP)U$YQG)i8tW8NeK)$Kew}5tyX0DUv_}k)4+fH!v6xP zD6jojzVyrZ*^?ijE>0GxKuVxTpm~F8IqJH`BR~8Vy!6lBgTQsbr2?2f{*q-GE-x=p zRaGneKB)WubN8mPwy$S>*zcYG(^_lqa|hcY4j76Q$#tbP3L$O7D4-#sK)_1XO8wGL zk*7ouY!gV^s49W94V0#Fqli+95`yv}C4?xE%H%;ffM`>-6;diia-3^o`*P3Od#&}K z-l-p+=NCX|+mwfR_34G+MzY6#`a!%o7rl%IqT_dmmKO6$J?tL6bxEn_x*Qa?r z`&K{+Q5LrlvCZK0zXfq4d!Kr_YQ5-5`|w0xYfdQ8y>l0ihNHN7=i-?6Du#I z-`DL3Pc0+6l?YB6Nh85^nz29Z@~ksae}w>AV@Tx(z_2tjp_XKA%6-`dNP?%>1zAXi z_x-ycM~uKne)+32=#n3)4Y;cXa2J1e1-$o@vBi0U;*2)uXyF?{6eYiP{V)8aXq z(^H;=&+QQSJtoWC)%Q=+g!wp^TkaOX9|hhse1e^zV)-n23DicHi&!w~5pCQ4u0HqQ z|EI}7@?C%V7xBrjdOu=}c*i^5f#3W;f0GU+&7a`_gpONAU9A;SRf&PuN}BOsZu~PJ z2$zom__JU42`EtrKB6+elmcjt&cQbgm+;;>%oD|YH4Tw>8m-}0=J_*$&^u36bwsxA z3tmAfi5Ll>S6VK78YO=L z9#Q~b|7(AXUQ3J^zwtl8r~dJ$fd8BS0UrW6u3lYC=hEi4DE3~LH~fJQl!>@0@AfNkDU0^;dDI1&I{VAq)M(n$vrrP0LuX*K*TyP#S0+D zr}txZTcmq^9!KnU14WWGhE6=iFRqeBidd3_ij%O|Afd4~To9MIsz}w?=2=m?Qru8C zYjX4ZzwD#%AovO`9BV8_HQuQgc%yUg7$fF+#ut72r}CeD{vUlgj5c5JYM}1> zmu12Ee5Mljb12^RZzHBzqI2h9SRo*!#QVPDWB41t`8D)4pXZJCBEDc=4ieOwL;=Vs z@U8#P-$wZ2h>z^P2KZIpw9^~-hA;4IxZ!2?CBS>%^|7Zw;0^x3y8g|Y&ufYghJc*4 z8NDOo8M|+3DWIi+51ymF=o@#9VQxXeDl}i7>F}1EA4nP;10R@vF9hRu26`B}d`j8X zVZYzALyyQk!_{F2Yh8X#&m!+h}DgQ++u8ct{huVAWSA9$H-CUNi$V^2%`u^l?Y_S zR&xBg77`!$vR}qu|L0#vo8dEbbIfYql+DSsBvFT7&+PlI%@2_JF@suP;M>0*`PnD7 z1Xp+fe-QYy@mn z?5T&J#}QZ8*ZgR^yAP@s?9^arFddm8U0ZOp-ZbiY@0uj?V zQ@^fh~`sn%=fPh^slY#=HlQg!7kr;l;XtZquXD#M&%8J8j znxVDC`FIPdG=|FL0EjrB&h%L*g}(2ICjdZ-bTGtxpvtP`Pgq7uQ4!T?oBmCC^R|H` zU_0_C%7V@Uj&!?8L3U4{=2BSG_ngC=5#q!atZA?hMVQ~W`BOjpTmRr4=(_F;+6sga zO5cAwQ}6r=gIyB%z?XkC<24c0oEsOZ(EJJgIeZj9|GxJ;cmIaOKD>PVTYu@Fz!;tP zvZ#&=MRha&+#mlaAZpqexqp`w2h`XVLl;(4>^Ln+-M4Y-y--3YZnM1IaNr)S(itI*BnNWMiEi~ z!_Xmk?(c)gI#VA*6+yZ2*JVNUjz64;b)F%mM9TptF?jeGp(Kwe`G%$qD=u!NZ3JmL zJt69`j9_yT`haBIc)4(z5p$~{@aNz4OZd@Ge?9%>r}?I^fL$$a4maqAZWCOhwa#?q zH?sNDINsdi>iP=xJK>y%)(YczMn4Sf!%2Uxv?LzDkitO75QNNqXLNlF zyE>T0;H4KIVI0YP=X85p);>SSX`G<7QRrjGJbd!%%g;Pe4AqN$!oMBwP- z46;B}gMP7<6~*GIu9SjQWJp5hj3vZ0mHyk1vE)+_xRi_p{-lxPD=0S5F9Gvm#yreW zZ4nU}V_>bt)z#HgH`qg)zdi)gc_!FC#>c|L&iC1C0igX~|m% z=)wW;y@zi+A_{ol^*w^~ z(3*HR&RXI-aMj|ir_!D`xv0kG0_6WmmHA8tB;WmC{%;XTQbZyYm?K)i+YZ}_$j!9yv{ zw>lkcMYpalT!c{C9lGl`{i>+Q0d+Lg5C8f%Y$BcWh$*oW4PyljkDXoy-u}+F8u_4;{{1TRDWE4 z@0_pN07B&{mW%WPF_QDPX$*ponW>j5Q>8Qmu(au8jBwu5^%coxkE#iUrfDH0V67*N zj3fKBCU71RIFDz95YU*0rT2jM0a6OA%Suc@kuv)(KDLl=%JkZ0E^!=h6eSW3Hs;y7 z0B|~=admy2-yF>q0tFqEKJ95#Hx^!Z40OK-9_Lj2fb^aRjwaKZ5>G_*g{3H&k;tet zMC7qh-R#B9g`lKF@Sb0H*tP+&p6epBC%@+_e*rN@{I!4fjYS9k(1=L^ue|aKuHStP zB_;ZO#07*naR3*TevMf^!eVG!_vfEC8uu&I@Cch}kYqL=yJ`u6;62<*Yqw>Cj})z^m<%U{hGX3Vjr_=|O8_uTxu+>0 zwF&9P?jVEB*QD_7>;k5t3=aT43{YCw4L|GT~7E%SdMoej|~b$C>7!_0OuZ z4>vLaK3LMNAs+D z=z4s`_y5B;y!gN9+dqYAnzENc*1SjD|Lb)=UV6t%c;|cHiSJQA4+{<4g0^V8tAAjXrpZfvOHYW2C1V0}c`SXq;V@~OL#%i4-{Xfsl)0!#(X2{fIpL;3a z%kNLeBX)-aPYT4-75`HWP7O3)j3(KHGwC7)6CRF$)}OPJWl9`F8`k9pd%j)lrIng1m_`j3Lq(%!1;89 zmIA|WfM6#g(>B!%AL!bKs{sPROFE1Pos(_6` z@phi9?%S@V^~wB`uJ7QT&wX^=qAnGY4Hw7%`8;B`+riEY&^C;vk@yW4gJoI$E(DJk z9$hndT`FZE$($zyk7=54NCy-^z8U;tl{AbQ==`-l2At0mOk;4oy@iyLcmiC(Ez?Z< z(lhOrOgcQgcQ8ocT}0O!pf!l-5M0PsMdRopjfRv8&RN)HMGT$@J>2K)c0Kt7K%nbL zo=DQiIr9vpl314&&RS+xNsM+ay_NScS`isSX?SM|Kx8mQ8=d8m1Sry|rCAK=7+A#? zgNNe?-1R;F*!TS`e)V@@!@QA$BK#Kw?uxkOu}85z5b6t4*7R;s#4EVNNaYeUz1Dy+ zlnFa8q&a%%@s;2IvuL!*?`h(FklZ#Ay7+ySbO?nIpp3?N9^t)bVhoMTfQWgX5JRZk zb%1qYu(~BF?)DJiTi-hLU5jO!(6kNQx}wqQ;;$@VA`FnYajj-#`ih9lV2E$VgCWjh;b^$kxKibIf^9 zvsD1rIkZMYH)IH6t%NoP^W^dks5qxQD%!+K%G^J9%=6bgifgn6f`^a_O>2mm2pSpn zT}$H;o-ZpoKq?8(WQpKCDGE7cA(1B9MxkwYSmq^{J83*+=Y`_M5g?7mJWn(_;)Nc3 zK+k@K9V4f9U9n6vnx=tNvVcet6>Yhi8i@qNJl$SxfTdVAR31A@pzmA!vA_5;_|+f$ z)A&7mB&;QmLJL=7OOs2p@VEibVnimDiDzv7-**6ZJJO9g2Z&oh%=beUU-^AML+MkZ z;7c{RtDLR0E}RQcT0ud9Q|ZU!VG#+nSw z-1PwG)_Z@>%RO*)C9+I-=1uSrUfF8HVwcjA6^{IN0%%Ntt`9qgKx}4ZG<$ z>^bg!T~;_}vyLNSU=jk7Df0w8YXt9LR~qMaT@T5@xT-oxz%%$50)(W`mas6g0k;d8 zgLEBj{P9F)w@Qk9XiVdTei*Q07NF+MFZhDed|5?M@{eLJfaKSltiS+Zzuy5dLU&D* z#S1j{Q+cz^^Ta@bMhGcAD6oZ?&yrwBz(kr~N>5J)peaDpv=nB*2`7Sq5%L{WkWeM# zyy}!hjPTAvO2y^|O&&c8E^%TEoQH1dg~l`viP>;OvgZIrHj$=5h{9Bh!J+TEtV#}C zss`sXB{nkSut|P)33$nFN#f#m$0lz5a@1K!Dq?#ups>i267RmpTP4u8bX!iANQmw^ z9{|p-gs_VZ+>x1*sEIbo8g5gj(g8Y86MppvzvIrIaSh;b0Q$a%)|&0bsRg+2@$dY_ z|COV$_+p5%0@UgQOk|u%q(hoXJKj4e5c?J00i48x!0}uuGqdk8jWedxiI^dheg&J% zaHwTl*A+qly5aO|Eny4^-PVN;ATlY@c`~w0BY}5p7G{iscNT3+hl&(}D_UBwyP?B8 z&*-{#qmE6iNUapK(eTz~!cXuX&O4Zf+<}RWt6*1$!+szTFpWHf6nQRg?ShHzhwF$U z(koMsD^*$US}P`cB-wCeWRjMQcOHG)GC~Z&$?YWb4VbXY zBQK*hy9hCjxZ)QmCAln@fQqHOixFB0Xw~IKOOpd;kb5Flw1(!fY1Cr)wdM5JSf?1p z4Gp%4IvAO)X3uex1Ak#x4S`@S{^<{W=e;HK)fKSYk>r?Ed)WMPfkyc0SCX~Ak;gy% zq3^`k{^vhO=`yj7bxD0}&B{z15RpSKVjwH7U%6-V1Hg+1V7F_tomY$z>-w;<6IGh^ z?Ha0?3_Ta;J)EP%yKNe1Dd61-tqmfxO(Ez)Sk@JdYG~8M2=7Tbrwp~Kqpm5-9nAgxW2y1Bp=BH znivVVSmzmL7>fF?`f#=*nYcmzMIx+JNI98`5c*1^#yzlQlyB}&nzs_VDr9k7OKiqW z0M1^5^nHwySgl24ntY?J>x_AtaeaNgIbdAU9DruJ&L!?a`Z# zM{ene5HUX8Q1fj0-VFet>wsYxvhCHSZ(W^m6hegegu_`2EDJC$R5d*FP>3EP>g^4i zQTNK&g)<)#55ZXiUw`F?-hJ=4ev2rje-cbN=P*rVz;oB*-}~ObPc3MY-Bz2v##XeX z1D`uPHkv7ym|!|a0PE)2uhNGI%RZMUl24!iMbt&QgSuz;e22)u7woD zb`^XB8?EWGlM*T!bTBB~7y_@2R?x=4I}f+6d7U*{W3?-k*4b-Q2$}IY!OU8%iO3c` z>1ZM-uR$A4_?c3O0t5oS_Ufy7PWb4BNBJ;FWQJ^ah3s8`ei(3hC)A^#$YBUc1adg+ z@c`i(hrpY|FS{kRE zPMCzmBR=%BQh0QIRh6zWzxMR}Aq0-+Gn{jH>E)LoVkFmOBo`xrVwHB;25gyRTR0~^ z#eT?qg$E7*4YF!C#tnIKT$j10Vu6{P-F)jFAAQkN@Cz zJ`?qS`DI|Y+vVGzB*+%W2TVE|qq751s`$3j;^GrwltK_3XQxmYpn@W}77n{tIrNafdzW}dFmHmd;yPNdo6p52~ZGW-?uoA zBZ3cTng$I64AXfel@Z_O-cis;StcdSFr7Ai-`)Y}L@e_H(-=ZKSrT0H{MU(A!fwBV zowxJXM3Gd?HRy&Ox@+J=z*t3<6~6*nm@mBWXameewj3h_c44q*&b;aL?_PLxg=w5h z3D4}Ud73c{J*H{N`s_0DDea2bEX|1E$?Zry&A+)&BHl&h!N%D(m4bJYk$`&-fwr9? z<%0P&RL0#;D?eo1{_I-(vXRupfO(qn@gIEmv&WFvz|~ccwrwd;gU7YWc<3P|%BJRu zRjwtr>OTSC_Q{icxm{gf4ezmI zWU%LXc+aY&{cgZCPH4L(Kf%6FXlN{v#MQ7Xy}`So!?}7h=JT0BJk5+i@;?urQijre zl8{QWIzH@&e5mw2F$CIR5{v4+v!u zbsd#&%Q!-U#)KF8P2X5)wS!2{cgZ8bXeyF^E73Gj1-aozl=6z z&Rn>E)2(?rv5n)dJ@@N`-pgOf4?ndKUH8z98!~7rnDdZ0{}Qpx;bY+G&%=)|?tzzI zHW-E>GZAU?Pk8*eIK1EW`05Y;J?{U@yt|H<&l}tacuQc~6+2YR>fbNZ%%E7&h@5V3 zvGT=MD&3gZg#_VL=i?E#H@7(59?^Ae{+{FMRB1?9IK(biWn{?sTB9+KBU+>J;)^ek z+g70WdD!*X?}v?srv63Mqk~E$H0M|*++iL^#>NEw{|0ZOHB9s#sz8RJhfX4mwrQx} zuny6cTO)b)#XxL;7c-D`U0|I@+cZ4t$o6Rn-Z6K8s>l5>KuCe$LmpL} zZjTT`VCY&hG^hwNX%vDGF?20bM2d_8jnRbm2Ab~=d;;%w178s#GZP7sr4iw}uCR8= z@lf;VGP2rsZDuKYjxh5sV4c~Jv27ufXCkA*(ML$d{ac_nyKP&vjmZo&?+9`eA!aW< zvEc-eW*${G!(ERN1m@k9b$3_PRbo}v;h1%TZ`Vx_r~BvbqS|2j+D)TRE-&qB@zKBf zXCB=Af1m^2@)nK#exLjPwE3SrvG~mV@Zr%l4UL^eycqkH(J@3Kv6ZSI5pV+kci@(^ zm~tp|iX^(uTAZ2elfY&n>5@bY*zJ1U-rQ2Fc{`;fN}+2SbVJLoLKgE#pL3Ifc^Ryw z112UsiS_@Y&{#=ZOo(Q1jzK0+q!61NcuqA*5lKm=7=x$&U#0jb${g(5hN+q{j|Qa> z6=hPOX($^oG3b)jm|b1gASBHK{VW=h7)6XHgsmhwA`p$&S*9LKtb9wUoL?`{G!26H zSXPVRJ<*zDKr84&+zkV~CvLc1S0Yd-0i|iA<2_X_S}XoQBAjE7_CRX1rYTa;;2pFm z@3|1$SvmwICw={gej2~@U;jr>Q*=Q{ezFgLR^;`4(Pbe77-P`30#}HeaHP$&B9i1f zgzEXmS{6k8K8qBfh~d(WGH#w#~Kc;M^5?!!OLnuD5b6fb1S75>72F|`2- z%RIw64`UQfD6GxBu#^g>QDj9I$ONa|?I5)x1KbdBKAw<5Y8ykGm*6d=NVEM0D`QB` zS}gO#uakh}E4Iix-X1ZJ5&bYgAi#1Pz26U57t0k1O$6Gu1n?$bP465O?;FC;7N#jT zkOFYa3f*_OzB-h$KZKIs5m^_ZsXEn2MjNq=s}%P}N|E)KXL1Au3K%q6W#rMtm=RL~ znTVpRv?8jZQtagyGchDh2xuNFAQWvDA1Zoq*FK}KuD;2>oASxGQq)88j>ntPuZ zJnTxhbTV2<&Oc4l<~VZdtBInVP}9hiU@ZiM5)hD(j0i{eEQlG&jpX6Z2chPm2En~; zYN;A60Zo9{W-M4QXkN!k2)1^*`!P*m(##uAyiIt4Qe;`y_dTwM5jW!n0Jlsz*x}s&FTg2c2R{O;ma_koX1St>`D=QMmU-FF}*eIc{D?M}c z(*}__>s(})p%??9{k(zKm14_V)8rdKR-j(`Z{VA^Z=39&uB3uiJ(t?tPurCljYJw! zQj(lg(FaoSR<4eiEomK9K*{B(H3rsM{_yC4bI!pKSs@c?1Xo;%`;R*mcIp4s1G9k1 z68o#h0`8Ok)@?w22-F9_qemlN8=syA{L+8(<~RQ3=5Lt@mJCO4ZqE3vSMQ%dq{laW z?@y!EW`n$MkiH6}lQF^tkD(>!93=#z_b^IAsfMao?-4!cHiW>1FZo}t>q5x8F#xFb zQ®-?FpV)$Z5vMn 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 literal 0 HcmV?d00001 diff --git a/code/beginner/tutorial5-textures/happy-tree.png b/code/beginner/tutorial5-textures/happy-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..fc86db345c14b95728250b8914fa284f34193834 GIT binary patch literal 28134 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 D;T5;R literal 0 HcmV?d00001 diff --git a/code/src/beginner/tutorial5-textures/main.rs b/code/beginner/tutorial5-textures/main.rs similarity index 93% rename from code/src/beginner/tutorial5-textures/main.rs rename to code/beginner/tutorial5-textures/main.rs index c0681006..9c994fdf 100644 --- a/code/src/beginner/tutorial5-textures/main.rs +++ b/code/beginner/tutorial5-textures/main.rs @@ -34,11 +34,11 @@ impl Vertex { } const VERTICES: &[Vertex] = &[ - Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [0.4131759, 1.0 - 0.99240386], }, // A - Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [0.0048659444, 1.0 - 0.56958646], }, // B - Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [0.28081453, 1.0 - 0.050602943], }, // C - Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [0.85967, 1.0 - 0.15267089], }, // D - Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [0.9414737, 1.0 - 0.7347359], }, // E + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E ]; const INDICES: &[u16] = &[ @@ -47,9 +47,22 @@ const INDICES: &[u16] = &[ 2, 3, 4, ]; +// const VERTICES: &[Vertex] = &[ +// Vertex { position: [-0.5, -0.5, 0.0], tex_coords: [0.0, 0.0]}, +// Vertex { position: [0.5, -0.5, 0.0], tex_coords: [2.0, 0.0]}, +// Vertex { position: [0.5, 0.5, 0.0], tex_coords: [2.0, 2.0]}, +// Vertex { position: [-0.5, 0.5, 0.0], tex_coords: [0.0, 2.0]}, +// ]; + +// const INDICES: &[u16] = &[ +// 2, 1, 0, +// 3, 2, 0, +// ]; + struct State { surface: wgpu::Surface, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -74,16 +87,13 @@ impl State { let size = window.inner_size(); let physical_size = size.to_physical(hidpi_factor); - let instance = wgpu::Instance::new(); + let surface = wgpu::Surface::create(window); - use raw_window_handle::HasRawWindowHandle as _; - let surface = instance.create_surface(window.raw_window_handle()); - - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: Default::default(), - }); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); - let mut device = adapter.request_device(&wgpu::DeviceDescriptor { + let (device, mut queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, @@ -145,7 +155,7 @@ impl State { size3d, ); - device.get_queue().submit(&[encoder.finish()]); + queue.submit(&[encoder.finish()]); let diffuse_texture_view = diffuse_texture.create_default_view(); let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor { @@ -252,6 +262,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -321,7 +332,7 @@ impl State { render_pass.draw_indexed(0..self.num_indices, 0, 0..1); } - self.device.get_queue().submit(&[ + self.queue.submit(&[ encoder.finish() ]); } diff --git a/code/src/beginner/tutorial5-textures/shader.frag b/code/beginner/tutorial5-textures/shader.frag similarity index 100% rename from code/src/beginner/tutorial5-textures/shader.frag rename to code/beginner/tutorial5-textures/shader.frag diff --git a/code/src/beginner/tutorial5-textures/shader.vert b/code/beginner/tutorial5-textures/shader.vert similarity index 100% rename from code/src/beginner/tutorial5-textures/shader.vert rename to code/beginner/tutorial5-textures/shader.vert diff --git a/code/intermediate/windowless/Cargo.toml b/code/intermediate/windowless/Cargo.toml new file mode 100644 index 00000000..817538fa --- /dev/null +++ b/code/intermediate/windowless/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "windowless" +version = "0.1.0" +authors = ["Ben Hansen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "0.22" +raw-window-handle = "0.1" +winit = "0.20.0-alpha3" +glsl-to-spirv = "0.1" +cgmath = "0.17" +wgpu = "0.4" + +[[bin]] +name = "windowless" +path = "main.rs" diff --git a/code/src/intermediate/windowless/main.rs b/code/intermediate/windowless/main.rs similarity index 95% rename from code/src/intermediate/windowless/main.rs rename to code/intermediate/windowless/main.rs index d70bb7ce..dae78340 100644 --- a/code/src/intermediate/windowless/main.rs +++ b/code/intermediate/windowless/main.rs @@ -1,7 +1,8 @@ fn main() { - let instance = wgpu::Instance::new(); - let adapter = instance.request_adapter(&Default::default()); - let mut device = adapter.request_device(&Default::default()); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); + let (device, mut queue) = adapter.request_device(&Default::default()); let texture_size = 256u32; let texture_desc = wgpu::TextureDescriptor { @@ -121,7 +122,7 @@ fn main() { texture_desc.size, ); - device.get_queue().submit(&[encoder.finish()]); + queue.submit(&[encoder.finish()]); output_buffer.map_read_async(0, output_buffer_size, move |result: wgpu::BufferMapAsyncResult<&[u8]>| { let mapping = result.unwrap(); diff --git a/code/src/intermediate/windowless/shader.frag b/code/intermediate/windowless/shader.frag similarity index 100% rename from code/src/intermediate/windowless/shader.frag rename to code/intermediate/windowless/shader.frag diff --git a/code/src/intermediate/windowless/shader.vert b/code/intermediate/windowless/shader.vert similarity index 100% rename from code/src/intermediate/windowless/shader.vert rename to code/intermediate/windowless/shader.vert diff --git a/code/src/beginner/tutorial5-textures/happy-tree.png b/code/src/beginner/tutorial5-textures/happy-tree.png deleted file mode 100644 index ead2f03b84e428c175f41e08d5d6af25e7eb47d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2278 zcmYLJ2~<-_7Onpi$R`OQ8FmE;s7JIxVQ7xnEC~df#e+nehR&mV4{vvX8i` z6Solo0P5~;&c^_NFcSiZD%SbMR)u1NtZ){K?apG+xv|lq;SnJKm=>lM+PaC@T3JfI zN8WLRIr>;ZIWa@!bWX+bazxwdes^`|OV88Yca7a+Vzm576bGGql>Kk(zo{c;h;eQ0 z@7;S`JnRnceUh^zoJ^BTSVy!XFyCP}*KWAE`c81);#)PzeW-riVP{5 zeWF?@G`T@{La*%eFcmpniO<*4-22V7WEsJr*=>K3-)@Uovocuw$8mx(bL6r0%5}}C zV>XnyroK-dRO+D0HtPX}1AqR78Z}Y1=E;rbV}wu#_Xkg(Ow+m>)cyPfUngnQ4MOXs zaW4xD_A+0WcHtyv{^RLWCefMpJva5cUB^dUL8Z^`Xf%_>sbluf?vPNPcFm8Je2}Ms z3p-+~+v+QaF=A8$54$=8C0GXoH_xa4iXm!TH=lC=kTka%1a21VVIU#S-P46IPJ$2{ ze12}@9SqWsb2$;mijIs7iHZZP*pPs@kYIX3cw89W)!mcroleokvNyUrJAR+|@A;vY zNV9ewbTmHPiLTNiq-4-3eYMZk5)ZbG(m4k$3tA_*K0e$0^^-67`JX>>TXMaiMBHMS z(7V=9eSb5^5jvGj3?r4 zbX^1x!gVQGof=dq+t8yW`_lfjSW!NAD8=4nrz#@6n=XH~Xjm(v;x3%c;wd6qOL|v6 z>GnbRq083{*bQ*gSg=f@lsFskk4l0$1a`bk>UVx;WY@h8&Tpbqkm=X*QuDG->D1qJ zHaiJTQP5>|<$P&j_^7PAM`8VVy>|IIPzxNl?%G(78cFh5sxwa@S{A@4w>4j}6e?*=R24dAzFV=Aav@OD?dbQ#YB>#ye8r0J4n6-gsKc*$3hIY27H9 zaTMN{uS+(e+F>)XVOd5q3vdb*A-a~iqy{isPXc<^ekLg6;_-m!$^=5I&nzbX_4GXX zS)8&670?!+0n-!`#}%}fLVh)8jM04pz*kzl^*&Iuhoun@RE;kCZ#LNBEaU~=*t#n- zBOwGZVUPx)t)c^fohTDTjy)g{K9Exd0E^dvjeCR*DyZ;=a#bvxqPeZiwxm{QOsG5l zWQZcnw*#;Mux;QWn4=5U`XCel?Pwex%wgJ+nP9=akxc29Fic(9vu?5o7C$#>h&(dI}OIGkz=9*mBD`C}$-AW^s!VAIDwImQI>Mya0M)Ixl(0B1 zwMq5{L=`5L2^J`=!#0hZ8&0qqZZXUN4Z53*OwVXs!T~LH^d6%R0`Fn*jFH3KELFNtkoiFa?ks0h%s(GX=^P=c@s86hT1TRc0j> z0b0ZA*zcVpL#A3suAJg{j#^&xy#SbOk(&dwD8fzzaK3RI^ zD?{ijqqDNpr%DA3-iE-B{OCSwgwnV&arKmQQG#Jlyr-)?wtr4{2ZZpetjw#D`3f4Z zVx{6ic)Nckz&GdTuFnNXQh99;M3EKIs>H`!hh*^VRm|+>-C-(;nOo-UJ8fUGzTHm% z4wL6QBO@DYXj-7icO!)@t`JHNRaZsQWjk!D;YdxNDP8f4U%>n(ID@% zE`T(;G#hpgmo{0+VWx?cGG||IfmuszvHl;7rF3See<^OlbAQx1JbEdSYWjs&Ir*i(^d`q;t%i~_2J_z4C1a5zQBXZOiT;n8I3JHjLVm2C6o*dKsR_>VSbym&oA)* zIf%ZKhCmXGVEd{2!Q}I5=sW|9MDI0c{!dfbcx$>w(d2*^a#28!L(uZYRTGc{U=B59 z9zh`hS;(*AU`~(i4g_sqs6$W|r_j~`M%(iN_c!tOv~n$wr{CcT%mNJ<(8EbrJYJa5 zZmA7O+8xJ1#2O8P9-789st5zyVst?swc|7xlZx~cwu1dFcsu=uCajGjwu@SFs#@M17l9n&|4~jg*hlVp=Z+zi>t382 + + + + diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 16bd12e2..8bae13a8 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -12,9 +12,8 @@ module.exports = { title: 'Beginner', collapsable: false, children: [ - '/beginner/', '/beginner/tutorial1-window', - '/beginner/tutorial2-swapchain', + '/beginner/tutorial2-swapchain/', '/beginner/tutorial3-pipeline/', '/beginner/tutorial4-buffer/', '/beginner/tutorial5-textures/', @@ -24,9 +23,10 @@ module.exports = { title: 'Intermediate', collapsable: false, children: [ - '/intermediate/windowless', + '/intermediate/windowless/', ], }, + '/news/' ] } } \ No newline at end of file diff --git a/docs/.vuepress/dist b/docs/.vuepress/dist index 1ab10bd5..be1d3f6d 160000 --- a/docs/.vuepress/dist +++ b/docs/.vuepress/dist @@ -1 +1 @@ -Subproject commit 1ab10bd51ebd6c97053daa5c6f0a60095d111964 +Subproject commit be1d3f6d4cf29ad9e5495bebf32e25dd23ee49b8 diff --git a/docs/README.md b/docs/README.md index 5ccde5c3..b0b8185e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,4 +10,4 @@ Wgpu actually has C bindings to allow you to write C/C++ code with it, as well a You should be fairly familiar with Rust before using this tutorial as I won't go into much detail on Rust syntax. If you're not super comfortable with Rust you can review the [Rust tutorial](https://www.rust-lang.org/learn). You should also be familiar about [Cargo](https://doc.rust-lang.org/cargo/). -I'm using this project as a way to learn wgpu myself, so I might miss some important details, or explain things wrong. I'm always open to constructive feedback. That being said, let's get started! \ No newline at end of file +I'm using this project as a way to learn wgpu myself, so I might miss some important details, or explain things wrong. I'm always open to constructive feedback. That being said, let's get started! diff --git a/docs/beginner/README.md b/docs/beginner/README.md deleted file mode 100644 index d416672e..00000000 --- a/docs/beginner/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# What we're covering - -## Just the basics -To make things simpler for everyone, this section of the guide will just cover things like setting up wgpu with a window, creating the swapchain, using the render pipeline, creating and using buffers, basic texturing, and basic lighting. \ No newline at end of file diff --git a/docs/beginner/tutorial1-window.md b/docs/beginner/tutorial1-window.md index 451599b7..58f84994 100644 --- a/docs/beginner/tutorial1-window.md +++ b/docs/beginner/tutorial1-window.md @@ -9,16 +9,20 @@ For the beginner stuff, we're going to keep things very simple, we'll add things ```toml [dependencies] image = "0.22" -raw-window-handle = "0.1" # needed to match wgpu's dependencies -winit = "0.20.0-alpha3" +winit = "0.20.0-alpha4" +wgpu = "0.4" +``` + +If you're on Windows, you can specify Vulkan as you desired backend by removing the `wgpu = "0.4"` and adding the following. +``` toml [dependencies.wgpu] -version = "0.3" +version = "0.4" features = ["vulkan"] ``` -## Why vulkan? -You need to specify what rendering backend you're using through [Cargo features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section) in order to run a program with wgpu. I'm specifying [vulkan](https://www.khronos.org/vulkan/), because I'm on [linux](https://www.linuxmint.com/). You're welcome to use [metal](https://developer.apple.com/metal/), or `DirectX 11/12` using `"metal"`, `"dx11"`, or `"dx12"` respectively. + ## What's with the "alpha" stuff? The wgpu examples use this version of [winit](https://github.com/rust-windowing/winit), so I elected to do the same. *Note: I'll update this once the changes to winit become fully released.* diff --git a/docs/beginner/tutorial2-swapchain.md b/docs/beginner/tutorial2-swapchain/README.md similarity index 91% rename from docs/beginner/tutorial2-swapchain.md rename to docs/beginner/tutorial2-swapchain/README.md index 3a932f36..14d6f4e0 100644 --- a/docs/beginner/tutorial2-swapchain.md +++ b/docs/beginner/tutorial2-swapchain/README.md @@ -9,6 +9,7 @@ struct State { surface: wgpu::Surface, adapter: wgpu::Adapter, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -59,30 +60,26 @@ impl State { The `hidpi_factor` is used to map "logical pixels" to actual pixels. We need this in tandem with `size` to get our `swap_chain` (more on that later) to be as accurate as possible. ```rust - let instance = wgpu::Instance::new(); + let surface = wgpu::Surface::create(window); - use raw_window_handle::HasRawWindowHandle as _; - let surface = instance.create_surface(window.raw_window_handle()); - - let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: Default::default(), - }); + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + ..Default::default() + }).unwrap(); ``` -The `instance`'s only use is to create a surface and request an `adapter`. We don't even need to save it. -The `surface` is used to create the `swap_chain`. We need the `window`'s `raw_window_handle` to access the native window implementation for `wgpu` to properly create the graphics backend. This is why we needed a window crate that supported [raw-window-handle](https://crates.io/crates/raw-window-handle). +The `surface` is used to create the `swap_chain`. Our `window` needs to implement [raw-window-handle](https://crates.io/crates/raw-window-handle)'s `HasRawWindowHandle` trait to access the native window implementation for `wgpu` to properly create the graphics backend. Fortunately, winit's `Window` fits the bill. -We need the `adapter` to create the device. +We need the `adapter` to create the device and queue. ```rust - let device = adapter.request_device(&wgpu::DeviceDescriptor { + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, limits: Default::default(), }); ``` -As of writing, the wgpu implementation doesn't allow you to customize much of requesting a device. Eventually the descriptor structs will be filled out more to allow you to find the optimal `device`. Even so, we still need the `device`, so we'll store it in the struct. +As of writing, the wgpu implementation doesn't allow you to customize much of requesting a device and queue. Eventually the descriptor structs will be filled out more to allow you to find the optimal device and queue. Even so, we still need them, so we'll store them in the struct. ```rust let sc_desc = wgpu::SwapChainDescriptor { @@ -108,6 +105,7 @@ At the end of the method, we simply return the resulting struct. Self { surface, device, + queue, sc_desc, swap_chain, hidpi_factor, @@ -273,7 +271,6 @@ Now we can actually get to clearing the screen (long time coming). We need to us }); } - self.device.get_queue().submit(&[ encoder.finish() ]); } @@ -304,7 +301,7 @@ event_loop.run(move |event, _, control_flow| { With all that, you should be getting something that looks like this. -![Window with a blue background](./tutorial2-swapchain-cleared-window.png) +![Window with a blue background](./cleared-window.png) ## Wait, what's going on with RenderPassDescriptor? diff --git a/docs/beginner/tutorial2-swapchain-cleared-window.png b/docs/beginner/tutorial2-swapchain/cleared-window.png similarity index 100% rename from docs/beginner/tutorial2-swapchain-cleared-window.png rename to docs/beginner/tutorial2-swapchain/cleared-window.png diff --git a/docs/beginner/tutorial3-pipeline/README.md b/docs/beginner/tutorial3-pipeline/README.md index fbfabe70..d4cdc744 100644 --- a/docs/beginner/tutorial3-pipeline/README.md +++ b/docs/beginner/tutorial3-pipeline/README.md @@ -93,6 +93,7 @@ This is the part where we finally make the thing in the title: the pipeline. Fir struct State { surface: wgpu::Surface, device: wgpu::Device, + queue: wgpu::Queue, sc_desc: wgpu::SwapChainDescriptor, swap_chain: wgpu::SwapChain, @@ -205,6 +206,7 @@ Now all we have to do is save the `render_pipeline` to `State` and then we can u Self { surface, device, + queue, sc_desc, swap_chain, // NEW! @@ -258,4 +260,4 @@ With all that you should be seeing a lovely brown triangle. ![Said lovely brown triangle](./tutorial3-pipeline-triangle.png) ## Challenge -Create a second pipeline that uses the triangles position data to create a color that it then sends to the fragment shader to use for `f_color`. Have the app swap between these when you press the spacebar. *Hint: use*`in`*and*`out`*variables.* \ No newline at end of file +Create a second pipeline that uses the triangles position data to create a color that it then sends to the fragment shader to use for `f_color`. Have the app swap between these when you press the spacebar. *Hint: use*`in`*and*`out`*variables in a separate shader.* \ No newline at end of file diff --git a/docs/beginner/tutorial4-buffer/README.md b/docs/beginner/tutorial4-buffer/README.md index 3e65d3bc..cde07069 100644 --- a/docs/beginner/tutorial4-buffer/README.md +++ b/docs/beginner/tutorial4-buffer/README.md @@ -68,6 +68,7 @@ Finally we add it to the returning struct. Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -182,6 +183,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -299,6 +301,7 @@ let num_indices = INDICES.len() as u32; Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -328,4 +331,4 @@ With all that you should have a garishly magenta pentagon in your window. ![Magenta pentagon in window](./indexed-pentagon.png) ## Challenge -Create a more complex shape than the one we made (aka. more than three triangles) using a vertex buffer and an index buffer. \ No newline at end of file +Create a more complex shape than the one we made (aka. more than three triangles) using a vertex buffer and an index buffer. Toggle between the two with the space key. \ No newline at end of file diff --git a/docs/beginner/tutorial5-textures/README.md b/docs/beginner/tutorial5-textures/README.md index c8f9c0a8..f3984d1b 100644 --- a/docs/beginner/tutorial5-textures/README.md +++ b/docs/beginner/tutorial5-textures/README.md @@ -58,10 +58,10 @@ let diffuse_buffer = device .fill_from_slice(&diffuse_rgba); ``` -We specified our `diffuse_buffer` to be `COPY_SRC` so that we can copy it to our `diffuse_texture`. We preform the copy using a `CommandEncoder`. We'll need to change `device`'s mutablility so we can submit the resulting `CommandBuffer`. +We specified our `diffuse_buffer` to be `COPY_SRC` so that we can copy it to our `diffuse_texture`. We preform the copy using a `CommandEncoder`. We'll need to change `queue`'s mutablility so we can submit the resulting `CommandBuffer`. ```rust -let mut device = // ... +let (device, mut queue) = // ... // ... @@ -117,6 +117,8 @@ The `address_mode_*` parameter's determine what to do if the sampler get's a tex * `Repeat`: The texture will repeat as texture coordinates exceed the textures dimensions. * `MirrorRepeat`: Similar to `Repeat`, but the image will flip when going over boundaries. +![address_mode.png](./address_mode.png) + The `mag_filter` and `min_filter` options describe what to do when a fragment covers multiple pixels, or there are multiple fragments for one pixel respectively. This often comes into play when viewing a surface from up close, or far away. There are 2 options: * `Linear`: This option will attempt to blend the in-between fragments so that they seem to flow together. * `Nearest`: In-between fragments will use the color of the nearest pixel. This creates an image that's crisper from far away, but pixelated when view from close up. This can be desirable however if your textures are designed to be pixelated such is in pixel art games, or voxel games like Minecraft. @@ -196,6 +198,7 @@ impl State { Self { surface, device, + queue, sc_desc, swap_chain, render_pipeline, @@ -334,7 +337,11 @@ If we run our program now we should get the following result. ![an upside down tree on a hexagon](./upside-down.png) -That's weird, our tree is upside down! This is because wgpu's coordinate system has positive y values going down while texture coords have y as up. We can get our triangle right-side up by inverting the y coord of each texture coord. +That's weird, our tree is upside down! This is because wgpu's coordinate system has positive y values going down while texture coords have y as up. + +![happy-tree-uv-coords.png](./happy-tree-uv-coords.png) + +We can get our triangle right-side up by inverting the y coord of each texture coord. ```rust const VERTICES: &[Vertex] = &[ @@ -346,10 +353,26 @@ const VERTICES: &[Vertex] = &[ ]; ``` +Simplifying that gives us. + +```rust +const VERTICES: &[Vertex] = &[ + Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A + Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B + Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C + Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D + Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E +]; +``` + With that in place we now have our tree subscribed right-side up on our hexagon. ![our happy tree as it should be](./rightside-up.png) +## Challenge + +Create another texture and swap it out when you press the space key. +