From 917b3e3c44b4a6fc6ccfacc88adbf3ec35d81cae Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Sun, 6 Jun 2021 12:00:24 -0400 Subject: [PATCH] Revert "Merge branch 'imbris/wgpu-master-rebased' into 'master'" This reverts commit e7a766b3104d02fc78f5769de4811f983cd989e9, reversing changes made to 24c256b681b687f45d18d8b1bcd7d9b351968d26. --- .cargo/config | 6 +- .gitlab-ci.yml | 1 - .gitlab/CI/build.gitlab-ci.yml | 8 - CHANGELOG.md | 2 - Cargo.lock | 782 ++--- Cargo.toml | 42 +- assets/voxygen/i18n/en/hud/hud_settings.ron | 6 - assets/voxygen/shaders/antialias/fxaa.glsl | 26 +- .../voxygen/shaders/antialias/msaa-x16.glsl | 38 +- assets/voxygen/shaders/antialias/msaa-x4.glsl | 14 +- assets/voxygen/shaders/antialias/msaa-x8.glsl | 22 +- assets/voxygen/shaders/antialias/none.glsl | 8 +- assets/voxygen/shaders/blit-frag.glsl | 16 - assets/voxygen/shaders/blit-vert.glsl | 15 - assets/voxygen/shaders/clouds-frag.glsl | 39 +- assets/voxygen/shaders/clouds-vert.glsl | 17 +- assets/voxygen/shaders/debug-frag.glsl | 19 - assets/voxygen/shaders/debug-vert.glsl | 20 - assets/voxygen/shaders/figure-frag.glsl | 31 +- assets/voxygen/shaders/figure-vert.glsl | 30 +- assets/voxygen/shaders/fluid-frag/cheap.glsl | 14 +- assets/voxygen/shaders/fluid-frag/shiny.glsl | 36 +- assets/voxygen/shaders/fluid-vert.glsl | 10 +- .../shaders/include/cloud/regular.glsl | 17 +- assets/voxygen/shaders/include/globals.glsl | 9 +- assets/voxygen/shaders/include/light.glsl | 7 +- assets/voxygen/shaders/include/lod.glsl | 91 +- assets/voxygen/shaders/include/random.glsl | 11 +- assets/voxygen/shaders/include/shadows.glsl | 80 +- assets/voxygen/shaders/include/sky.glsl | 13 +- assets/voxygen/shaders/include/srgb.glsl | 17 +- .../shaders/light-shadows-directed-frag.glsl | 48 + .../shaders/light-shadows-directed-vert.glsl | 21 +- .../shaders/light-shadows-figure-vert.glsl | 22 +- .../voxygen/shaders/light-shadows-frag.glsl | 2 +- .../voxygen/shaders/light-shadows-vert.glsl | 4 +- assets/voxygen/shaders/lod-terrain-frag.glsl | 12 +- assets/voxygen/shaders/lod-terrain-vert.glsl | 26 +- assets/voxygen/shaders/particle-frag.glsl | 14 +- assets/voxygen/shaders/particle-vert.glsl | 30 +- .../shaders/point-light-shadows-vert.glsl | 61 - assets/voxygen/shaders/postprocess-frag.glsl | 42 +- assets/voxygen/shaders/postprocess-vert.glsl | 16 +- assets/voxygen/shaders/skybox-frag.glsl | 11 +- assets/voxygen/shaders/skybox-vert.glsl | 31 +- assets/voxygen/shaders/sprite-frag.glsl | 133 +- assets/voxygen/shaders/sprite-vert.glsl | 257 +- assets/voxygen/shaders/terrain-frag.glsl | 23 +- assets/voxygen/shaders/terrain-vert.glsl | 19 +- assets/voxygen/shaders/ui-frag.glsl | 21 +- assets/voxygen/shaders/ui-vert.glsl | 36 +- assets/voxygen/texture/waves.png | 3 + common/base/src/lib.rs | 11 +- common/frontend/src/lib.rs | 3 +- voxygen/Cargo.toml | 22 +- voxygen/anim/Cargo.toml | 1 - voxygen/anim/src/lib.rs | 7 +- voxygen/benches/meshing_benchmark.rs | 11 +- voxygen/src/hud/mod.rs | 36 +- voxygen/src/hud/settings_window/interface.rs | 28 +- voxygen/src/hud/settings_window/video.rs | 161 +- voxygen/src/lib.rs | 9 +- voxygen/src/menu/char_selection/mod.rs | 37 +- voxygen/src/menu/char_selection/ui/mod.rs | 5 +- voxygen/src/menu/main/mod.rs | 142 +- voxygen/src/menu/main/scene.rs | 28 - voxygen/src/menu/main/ui/mod.rs | 6 +- voxygen/src/mesh/greedy.rs | 20 +- voxygen/src/mesh/mod.rs | 23 +- voxygen/src/mesh/segment.rs | 599 ++-- voxygen/src/mesh/terrain.rs | 432 +-- voxygen/src/render/bound.rs | 14 - voxygen/src/render/buffer.rs | 63 - voxygen/src/render/consts.rs | 34 +- voxygen/src/render/error.rs | 104 +- voxygen/src/render/instances.rs | 42 +- voxygen/src/render/mesh.rs | 148 +- voxygen/src/render/mod.rs | 91 +- voxygen/src/render/model.rs | 112 +- voxygen/src/render/pipelines/blit.rs | 123 - voxygen/src/render/pipelines/clouds.rs | 237 +- voxygen/src/render/pipelines/debug.rs | 172 - voxygen/src/render/pipelines/figure.rs | 231 +- voxygen/src/render/pipelines/fluid.rs | 158 +- voxygen/src/render/pipelines/lod_terrain.rs | 277 +- voxygen/src/render/pipelines/mod.rs | 492 +-- voxygen/src/render/pipelines/particle.rs | 241 +- voxygen/src/render/pipelines/postprocess.rs | 201 +- voxygen/src/render/pipelines/shadow.rs | 396 +-- voxygen/src/render/pipelines/skybox.rs | 148 +- voxygen/src/render/pipelines/sprite.rs | 374 +- voxygen/src/render/pipelines/terrain.rs | 230 +- voxygen/src/render/pipelines/ui.rs | 230 +- voxygen/src/render/renderer.rs | 3025 +++++++++++------ voxygen/src/render/renderer/binding.rs | 88 - voxygen/src/render/renderer/drawer.rs | 869 ----- voxygen/src/render/renderer/locals.rs | 75 - .../src/render/renderer/pipeline_creation.rs | 914 ----- voxygen/src/render/renderer/screenshot.rs | 185 - voxygen/src/render/renderer/shaders.rs | 96 - voxygen/src/render/renderer/shadow_map.rs | 278 -- voxygen/src/render/texture.rs | 348 +- voxygen/src/run.rs | 13 +- voxygen/src/scene/camera.rs | 267 +- voxygen/src/scene/debug.rs | 131 - voxygen/src/scene/figure/cache.rs | 83 +- voxygen/src/scene/figure/mod.rs | 248 +- voxygen/src/scene/lod.rs | 19 +- voxygen/src/scene/math.rs | 2 +- voxygen/src/scene/mod.rs | 837 +++-- voxygen/src/scene/particle.rs | 35 +- voxygen/src/scene/simple.rs | 146 +- voxygen/src/scene/terrain.rs | 737 ++-- voxygen/src/session/mod.rs | 33 +- voxygen/src/session/settings_change.rs | 4 - voxygen/src/settings/interface.rs | 2 - voxygen/src/ui/cache.rs | 34 +- voxygen/src/ui/graphic/mod.rs | 73 +- voxygen/src/ui/ice/cache.rs | 38 +- voxygen/src/ui/ice/mod.rs | 12 +- voxygen/src/ui/ice/renderer/mod.rs | 72 +- voxygen/src/ui/mod.rs | 93 +- voxygen/src/ui/scale.rs | 5 + voxygen/src/window.rs | 178 +- 124 files changed, 6134 insertions(+), 10784 deletions(-) delete mode 100644 assets/voxygen/shaders/blit-frag.glsl delete mode 100644 assets/voxygen/shaders/blit-vert.glsl delete mode 100644 assets/voxygen/shaders/debug-frag.glsl delete mode 100644 assets/voxygen/shaders/debug-vert.glsl create mode 100644 assets/voxygen/shaders/light-shadows-directed-frag.glsl delete mode 100644 assets/voxygen/shaders/point-light-shadows-vert.glsl create mode 100644 assets/voxygen/texture/waves.png delete mode 100644 voxygen/src/menu/main/scene.rs delete mode 100644 voxygen/src/render/bound.rs delete mode 100644 voxygen/src/render/buffer.rs delete mode 100644 voxygen/src/render/pipelines/blit.rs delete mode 100644 voxygen/src/render/pipelines/debug.rs delete mode 100644 voxygen/src/render/renderer/binding.rs delete mode 100644 voxygen/src/render/renderer/drawer.rs delete mode 100644 voxygen/src/render/renderer/locals.rs delete mode 100644 voxygen/src/render/renderer/pipeline_creation.rs delete mode 100644 voxygen/src/render/renderer/screenshot.rs delete mode 100644 voxygen/src/render/renderer/shaders.rs delete mode 100644 voxygen/src/render/renderer/shadow_map.rs delete mode 100644 voxygen/src/scene/debug.rs diff --git a/.cargo/config b/.cargo/config index 7ce962b17f..263cedb682 100644 --- a/.cargo/config +++ b/.cargo/config @@ -9,11 +9,9 @@ csv-import = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv test-server = "run --bin veloren-server-cli --no-default-features -- -b" tracy-server = "-Zunstable-options run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" tracy-world-server = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile no_overflow -- -b" -test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd" -tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd --profile no_overflow" +test-voxygen = "run --bin veloren-voxygen --no-default-features --features gl,simd" +tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile no_overflow" server = "run --bin veloren-server-cli" -dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo" - [env] RUSTC_FORCE_INCREMENTAL = "1" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c497e9c1c1..d12687d107 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,6 @@ before_script: - export VELOREN_ASSETS="$(pwd)/assets" - echo "VELOREN_ASSETS=$VELOREN_ASSETS" - export RUSTFLAGS="-D warnings" - - export SHADERC_LIB_DIR=/shaderc/combined/ - rm -rf target || echo "it seems that sometimes OLD data is left over" # 8866215 is the user that is used to sync data to the collaboration repos diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index 516a74ad9f..6f374ee28b 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -75,25 +75,17 @@ coverage: .twindows: image: registry.gitlab.com/veloren/veloren-docker-ci/cache/release-windows:${CACHE_IMAGE_TAG} script: - - update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix - - update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix - ln -s /dockercache/target target - rm -r target/release/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - VELOREN_USERDATA_STRATEGY=executable cargo build --target=x86_64-pc-windows-gnu --release - cp -r target/x86_64-pc-windows-gnu/release/veloren-server-cli.exe $CI_PROJECT_DIR - cp -r target/x86_64-pc-windows-gnu/release/veloren-voxygen.exe $CI_PROJECT_DIR - - cp /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/libgcc_s_seh-1.dll $CI_PROJECT_DIR - - cp /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/libstdc++-6.dll $CI_PROJECT_DIR - - cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll $CI_PROJECT_DIR artifacts: paths: - veloren-server-cli.exe - veloren-voxygen.exe - assets/ - LICENSE - - libgcc_s_seh-1.dll - - libstdc++-6.dll - - libwinpthread-1.dll expire_in: 1 week .tmacos: diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e66c3154..1c44d1f6f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,8 +65,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Renamed Animal Trainers to Beastmasters and gave them their own set of armor to wear - ChargedRanged attacks (such as some bow attacks) use an FOV zoom effect to indicate charge. - Add chest to each dungeon with unique loot -- Added a new option in the graphics menu to enable GPU timing (not always supported). The timing values can be viewed in the HUD debug info (F3) and will be saved as chrome trace files in the working directory when taking a screenshot. -- Added new Present Mode option in the graphics menu. Selecting Fifo (i.e. vsync) or Mailbox can be used to eliminate screen tearing. ### Changed diff --git a/Cargo.lock b/Cargo.lock index b4bfc16740..266e2e40bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" + [[package]] name = "ansi-parser" version = "0.7.0" @@ -173,7 +179,7 @@ checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" dependencies = [ "proc-macro-hack", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -187,9 +193,6 @@ name = "arrayvec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" -dependencies = [ - "serde", -] [[package]] name = "as-slice" @@ -203,15 +206,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "ash" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06063a002a77d2734631db74e8f4ce7148b77fe522e6bca46f2ae7774fd48112" -dependencies = [ - "libloading 0.7.0", -] - [[package]] name = "assets_manager" version = "0.4.4" @@ -248,7 +242,7 @@ checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -368,21 +362,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.2.1" @@ -453,7 +432,7 @@ checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -536,10 +515,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cfg_aliases" -version = "0.1.1" +name = "cgl" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] [[package]] name = "chrono" @@ -669,6 +651,22 @@ dependencies = [ "cc", ] +[[package]] +name = "cocoa" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54201c07dcf3a5ca33fececb8042aed767ee4bfd5a0235a8ceabcda956044b2" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation 0.9.1", + "core-graphics 0.22.2", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -700,16 +698,6 @@ dependencies = [ "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -780,12 +768,6 @@ dependencies = [ "walkdir 0.1.8", ] -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - [[package]] name = "copypasta" version = "0.7.1" @@ -1226,24 +1208,13 @@ dependencies = [ "sct", ] -[[package]] -name = "d3d12" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091ed1b25fe47c7ff129fc440c23650b6114f36aa00bc7212cc8041879294428" -dependencies = [ - "bitflags", - "libloading 0.7.0", - "winapi 0.3.9", -] - [[package]] name = "daggy" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9293a0da7d1bc1f30090ece4d9f9de79a07be7302ddb00e5eb1fefb6ee6409e2" dependencies = [ - "petgraph 0.4.13", + "petgraph", ] [[package]] @@ -1277,7 +1248,7 @@ dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", "strsim 0.9.3", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1291,7 +1262,7 @@ dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", "strsim 0.10.0", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1302,7 +1273,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1313,7 +1284,7 @@ checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" dependencies = [ "darling_core 0.12.4", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1343,7 +1314,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1437,6 +1408,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "draw_state" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cf9537e2d06891448799b96d5a8c8083e0e90522a7fdabe6ebf4f41d79d651" +dependencies = [ + "bitflags", +] + [[package]] name = "either" version = "1.6.1" @@ -1475,7 +1455,7 @@ checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1496,7 +1476,7 @@ dependencies = [ "darling 0.12.4", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1572,7 +1552,7 @@ checksum = "ccb5acb1045ebbfa222e2c50679e392a71dd77030b78fb0189f2d9c5974400f9" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1599,12 +1579,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -[[package]] -name = "fixedbitset" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" - [[package]] name = "flate2" version = "1.0.20" @@ -1766,7 +1740,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -1872,147 +1846,45 @@ dependencies = [ ] [[package]] -name = "gfx-auxil" -version = "0.9.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" +name = "gfx" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de46f9508a5c259aef105f0bff760ceddca832ea9c87ce03d1923e22ee155b" dependencies = [ - "fxhash", - "gfx-hal", - "spirv_cross", -] - -[[package]] -name = "gfx-backend-dx11" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "gfx-auxil", - "gfx-hal", - "libloading 0.7.0", + "draw_state", + "gfx_core", "log", - "parking_lot 0.11.1", - "range-alloc", - "raw-window-handle", - "smallvec", - "spirv_cross", - "thunderdome", - "winapi 0.3.9", - "wio", ] [[package]] -name = "gfx-backend-dx12" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags", - "d3d12", - "gfx-auxil", - "gfx-hal", - "log", - "parking_lot 0.11.1", - "range-alloc", - "raw-window-handle", - "smallvec", - "spirv_cross", - "thunderdome", - "winapi 0.3.9", -] - -[[package]] -name = "gfx-backend-empty" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "gfx-hal", - "log", - "raw-window-handle", -] - -[[package]] -name = "gfx-backend-gl" -version = "0.8.1" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "fxhash", - "gfx-auxil", - "gfx-hal", - "glow", - "js-sys", - "khronos-egl", - "libloading 0.7.0", - "log", - "naga", - "parking_lot 0.11.1", - "raw-window-handle", - "spirv_cross", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gfx-backend-metal" -version = "0.8.1" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "block", - "cocoa-foundation", - "copyless", - "foreign-types", - "fxhash", - "gfx-auxil", - "gfx-hal", - "log", - "metal", - "naga", - "objc", - "parking_lot 0.11.1", - "profiling", - "range-alloc", - "raw-window-handle", - "spirv_cross", - "storage-map", -] - -[[package]] -name = "gfx-backend-vulkan" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "ash", - "byteorder", - "core-graphics-types", - "gfx-hal", - "inplace_it", - "libloading 0.7.0", - "log", - "naga", - "objc", - "parking_lot 0.11.1", - "raw-window-handle", - "renderdoc-sys", - "smallvec", - "winapi 0.3.9", -] - -[[package]] -name = "gfx-hal" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" +name = "gfx_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75fbddaef2e12b4995900539d7209d947b988a3d87ee8737484d049b526e5441" dependencies = [ "bitflags", - "naga", - "raw-window-handle", - "thiserror", + "draw_state", + "log", +] + +[[package]] +name = "gfx_device_gl" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c385fa380c18888633aa27d1e16cbae518469702a2f69dcb5f52d5378bebc" +dependencies = [ + "gfx_core", + "gfx_gl", + "log", +] + +[[package]] +name = "gfx_gl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d38164670920cfb7491bc0cf6f49f0554bd1c44cdbedc6c78d2bf91691ff5e" +dependencies = [ + "gl_generator", ] [[package]] @@ -2079,6 +1951,17 @@ dependencies = [ "url", ] +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + [[package]] name = "glam" version = "0.10.2" @@ -2095,15 +1978,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] -name = "glow" -version = "0.9.0" +name = "glsl-include" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b80b98efaa8a34fce11d60dd2ce2760d5d83c373cbcc73bb87c2a3a84a54108" +checksum = "daa2afb1631e7ab4543e0dde0e3fc68bb49c58fee89c07f30a26553b1f684ab6" dependencies = [ - "js-sys", - "slotmap 0.4.0", - "wasm-bindgen", - "web-sys", + "lazy_static", + "regex", +] + +[[package]] +name = "glutin" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae1cbb9176b9151c4ce03f012e3cd1c6c18c4be79edeaeb3d99f5d8085c5fa3" +dependencies = [ + "android_glue", + "cgl", + "cocoa 0.23.0", + "core-foundation 0.9.1", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.6.7", + "log", + "objc", + "osmesa-sys", + "parking_lot 0.11.1", + "wayland-client 0.28.5", + "wayland-egl", + "winapi 0.3.9", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" +dependencies = [ + "gl_generator", + "winapi 0.3.9", +] + +[[package]] +name = "glutin_emscripten_sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" +dependencies = [ + "gl_generator", + "objc", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +dependencies = [ + "gl_generator", ] [[package]] @@ -2145,45 +2098,6 @@ dependencies = [ "xi-unicode", ] -[[package]] -name = "gpu-alloc" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc1b6ca374e81862526786d9cb42357ce03706ed1b8761730caafd02ab91f3a" -dependencies = [ - "bitflags", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" -dependencies = [ - "bitflags", -] - -[[package]] -name = "gpu-descriptor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2" -dependencies = [ - "bitflags", - "gpu-descriptor-types", - "hashbrown 0.9.1", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" -dependencies = [ - "bitflags", -] - [[package]] name = "guillotiere" version = "0.6.1" @@ -2522,12 +2436,6 @@ dependencies = [ "libc", ] -[[package]] -name = "inplace_it" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" - [[package]] name = "instant" version = "0.1.9" @@ -2649,14 +2557,10 @@ dependencies = [ ] [[package]] -name = "khronos-egl" -version = "4.1.0" +name = "khronos_api" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" -dependencies = [ - "libc", - "libloading 0.7.0", -] +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "lazy-bytes-cast" @@ -2932,20 +2836,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "metal" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "foreign-types", - "log", - "objc", -] - [[package]] name = "minifb" version = "0.19.1" @@ -3061,30 +2951,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" -[[package]] -name = "naga" -version = "0.4.0" -source = "git+https://github.com/gfx-rs/naga?tag=gfx-25#057d03ad86f18e3bb3866b20901d8d4e892dd3d6" -dependencies = [ - "bit-set", - "bitflags", - "codespan-reporting", - "fxhash", - "log", - "num-traits", - "petgraph 0.5.1", - "rose_tree", - "spirv_headers", - "thiserror", -] - [[package]] name = "native-dialog" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d689b01017db3e350e0e9798d233cca9ad3bf810e7c02b9b55ec06b9ee7dbd8a" dependencies = [ - "cocoa", + "cocoa 0.24.0", "dirs-next", "objc", "objc-foundation", @@ -3159,7 +3032,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -3386,7 +3259,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -3494,7 +3367,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -3506,7 +3379,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -3516,7 +3389,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", - "objc_exception", ] [[package]] @@ -3530,15 +3402,6 @@ dependencies = [ "objc_id", ] -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - [[package]] name = "objc_id" version = "0.1.1" @@ -3599,6 +3462,17 @@ dependencies = [ "byteorder", ] +[[package]] +name = "old_school_gfx_glutin_ext" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "450a2a0e6805771787b965af9a552581c9dfc588dc33761c1be690117cd792e1" +dependencies = [ + "gfx_core", + "gfx_device_gl", + "glutin", +] + [[package]] name = "once_cell" version = "1.7.2" @@ -3650,6 +3524,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +dependencies = [ + "shared_library", +] + [[package]] name = "owned_ttf_parser" version = "0.6.0" @@ -3765,17 +3648,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" dependencies = [ - "fixedbitset 0.1.9", -] - -[[package]] -name = "petgraph" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" -dependencies = [ - "fixedbitset 0.2.0", - "indexmap", + "fixedbitset", ] [[package]] @@ -3795,7 +3668,7 @@ checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -3915,7 +3788,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", "version_check 0.9.3", ] @@ -3960,26 +3833,6 @@ dependencies = [ "unicode-xid 0.2.2", ] -[[package]] -name = "profiling" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a66d5e88679f2720126c11ee29da07a08f094eac52306b066edd7d393752d6" -dependencies = [ - "profiling-procmacros", - "tracy-client", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b83296b4ea7c60800e0832617bb9d274e4f5928abd08acc5a6ae8be0fb15969" -dependencies = [ - "quote 1.0.9", - "syn 1.0.72", -] - [[package]] name = "prometheus" version = "0.12.0" @@ -4179,11 +4032,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "range-alloc" -version = "0.1.2" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" - [[package]] name = "raw-window-handle" version = "0.3.3" @@ -4293,7 +4141,7 @@ dependencies = [ "quote 1.0.9", "refinery-core", "regex", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -4354,12 +4202,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "renderdoc-sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" - [[package]] name = "ring" version = "0.16.20" @@ -4402,15 +4244,6 @@ version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84348444bd7ad45729d0c49a4240d7cdc11c9d512c06c5ad1835c1ad4acda6db" -[[package]] -name = "rose_tree" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284de9dae38774e2813aaabd7e947b4a6fe9b8c58c2309f754a487cdd50de1c2" -dependencies = [ - "petgraph 0.5.1", -] - [[package]] name = "rusqlite" version = "0.24.2" @@ -4739,7 +4572,7 @@ checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -4761,7 +4594,7 @@ checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -4770,26 +4603,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -[[package]] -name = "shaderc" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b8aeaae10b9bda5cba66736a7e265f67698e912e1cc6a4678acba286e22be9" -dependencies = [ - "libc", - "shaderc-sys", -] - -[[package]] -name = "shaderc-sys" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b12d7c62d6732884c9dfab587503fa3a795b108df152415a89da23812d4737e" -dependencies = [ - "cmake", - "libc", -] - [[package]] name = "sharded-slab" version = "0.1.1" @@ -4799,6 +4612,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + [[package]] name = "shell-words" version = "1.0.0" @@ -4843,7 +4666,7 @@ checksum = "d5404c36bd155e41a54276ab6aafedad2fb627e5e5849d36ec439c9ddc044a2f" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -4894,12 +4717,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" -[[package]] -name = "slotmap" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" - [[package]] name = "slotmap" version = "1.0.3" @@ -4990,7 +4807,7 @@ source = "git+https://github.com/amethyst/specs.git?rev=5a9b71035007be0e3574f351 dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5017,27 +4834,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "spirv_cross" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60647fadbf83c4a72f0d7ea67a7ca3a81835cf442b8deae5c134c3e0055b2e14" -dependencies = [ - "cc", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "spirv_headers" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f5b132530b1ac069df335577e3581765995cba5a13995cdbbdbc8fb057c532c" -dependencies = [ - "bitflags", - "num-traits", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -5091,7 +4887,7 @@ dependencies = [ "quote 1.0.9", "serde", "serde_derive", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5107,7 +4903,7 @@ dependencies = [ "serde_derive", "serde_json", "sha1", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5116,15 +4912,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" -[[package]] -name = "storage-map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418bb14643aa55a7841d5303f72cf512cfb323b8cc221d51580500a1ca75206c" -dependencies = [ - "lock_api 0.4.4", -] - [[package]] name = "str-buf" version = "1.0.5" @@ -5170,7 +4957,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5188,7 +4975,7 @@ dependencies = [ "heck", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5216,9 +5003,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.72" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", @@ -5297,7 +5084,7 @@ checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5309,12 +5096,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "thunderdome" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b4947742c93ece24a0032141d9caa3d853752e694a57e35029dd2bd08673e0" - [[package]] name = "time" version = "0.1.43" @@ -5377,7 +5158,7 @@ checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5462,7 +5243,7 @@ checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -5780,7 +5561,7 @@ dependencies = [ "serde", "serde_repr", "slab", - "slotmap 1.0.3", + "slotmap", "specs", "specs-idvs", "spin_sleep", @@ -5975,7 +5756,7 @@ version = "0.1.0" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -6062,7 +5843,6 @@ version = "0.9.0" dependencies = [ "backtrace", "bincode", - "bytemuck", "chrono", "conrod_core", "conrod_winit", @@ -6077,8 +5857,12 @@ dependencies = [ "dot_vox", "enum-iterator", "euc", - "futures-executor", + "gfx", + "gfx_device_gl", + "gfx_gl", "gilrs", + "glsl-include", + "glutin", "glyph_brush", "guillotiere", "hashbrown 0.9.1", @@ -6091,14 +5875,13 @@ dependencies = [ "native-dialog", "num 0.4.0", "num_cpus", + "old_school_gfx_glutin_ext", "ordered-float 2.5.1", - "profiling", "rand 0.8.3", "rayon", "rodio", "ron", "serde", - "shaderc", "specs", "specs-idvs", "strum", @@ -6119,8 +5902,6 @@ dependencies = [ "veloren-server", "veloren-voxygen-anim", "veloren-world", - "wgpu", - "wgpu-profiler", "window_clipboard 0.2.1", "winit", "winres", @@ -6130,7 +5911,6 @@ dependencies = [ name = "veloren-voxygen-anim" version = "0.9.0" dependencies = [ - "bytemuck", "find_folder", "lazy_static", "libloading 0.7.0", @@ -6277,7 +6057,7 @@ dependencies = [ "log", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", "wasm-bindgen-shared", ] @@ -6311,7 +6091,7 @@ checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6390,7 +6170,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.27", "quote 1.0.9", - "syn 1.0.72", + "syn 1.0.69", ] [[package]] @@ -6597,6 +6377,16 @@ dependencies = [ "xcursor", ] +[[package]] +name = "wayland-egl" +version = "0.28.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9461a67930ec16da7a4fd8b50e9ffa23f4417240b43ec84008bd1b2c94421c94" +dependencies = [ + "wayland-client 0.28.5", + "wayland-sys 0.28.5", +] + [[package]] name = "wayland-protocols" version = "0.27.0" @@ -6693,75 +6483,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "wgpu" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu-rs.git?rev=7486bdad64bb5d17b709ecccb41e063469efff88#7486bdad64bb5d17b709ecccb41e063469efff88" -dependencies = [ - "arrayvec", - "js-sys", - "log", - "naga", - "parking_lot 0.11.1", - "raw-window-handle", - "serde", - "smallvec", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wgpu-core", - "wgpu-types", -] - -[[package]] -name = "wgpu-core" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" -dependencies = [ - "arrayvec", - "bitflags", - "cfg_aliases", - "copyless", - "fxhash", - "gfx-backend-dx11", - "gfx-backend-dx12", - "gfx-backend-empty", - "gfx-backend-gl", - "gfx-backend-metal", - "gfx-backend-vulkan", - "gfx-hal", - "gpu-alloc", - "gpu-descriptor", - "log", - "naga", - "parking_lot 0.11.1", - "profiling", - "raw-window-handle", - "ron", - "serde", - "smallvec", - "thiserror", - "wgpu-types", -] - -[[package]] -name = "wgpu-profiler" -version = "0.4.0" -source = "git+https://github.com/Imberflur/wgpu-profiler?tag=wgpu-0.8#b156eb145bc223386ef344860d9b33b3c181650c" -dependencies = [ - "futures", - "wgpu", -] - -[[package]] -name = "wgpu-types" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" -dependencies = [ - "bitflags", - "serde", -] - [[package]] name = "which" version = "4.1.0" @@ -6856,7 +6577,7 @@ version = "0.24.0" source = "git+https://gitlab.com/veloren/winit.git?branch=macos-test-spiffed#488c511802dfd95ca54f6f76a38547c93c7b02c9" dependencies = [ "bitflags", - "cocoa", + "cocoa 0.24.0", "core-foundation 0.9.1", "core-graphics 0.22.2", "core-video-sys", @@ -6890,15 +6611,6 @@ dependencies = [ "toml", ] -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 88c76c276e..dffbf6ba0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,6 +98,11 @@ incremental = true inherits = 'release' debug = 1 +[patch.crates-io] +# macos CI fix isn't merged yet +winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } +vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } + [workspace.metadata.nix] systems = ["x86_64-linux"] @@ -108,40 +113,3 @@ key = "veloren-nix.cachix.org-1:zokfKJqVsNV6kI/oJdLF6TYBdNPYGSb+diMVQPn/5Rc=" [workspace.metadata.nix.crateOverride.veloren-network] buildInputs = ["openssl"] nativeBuildInputs = ["pkg-config"] - -[workspace.metadata.nix.crateOverride.shaderc-sys] -buildInputs = ["shaderc"] -nativeBuildInputs = ["cmake", "python3", "gnumake"] - -[patch.crates-io] -# macos CI fix isn't released yet -winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } -vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } -# patch wgpu so we can use wgpu-profiler crate -wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "7486bdad64bb5d17b709ecccb41e063469efff88" } - -# # use the latest fixes in naga (remove when updates trickle down to wgpu-rs) -# naga = { git = "https://github.com/gfx-rs/naga.git", rev = "3a0f0144112ff621dd7f731bf455adf6cab19164" } -# # use the latest fixes in gfx (remove when updates trickle down to wgpu-rs) -# gfx-hal = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } -# gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" } - -# # Uncomment this to use a local fork of wgpu (for testing purposes) -# [patch.'https://github.com/gfx-rs/wgpu'] -# wgpu-core = { path = "../wgpu/wgpu-core" } -# wgpu-types = { path = "../wgpu/wgpu-types" } - -# # Uncomment this to use a local fork of gfx-hal (for testing purposes) -# [patch."https://github.com/gfx-rs/gfx"] -# gfx-hal = { path = "../gfx/src/hal" } -# gfx-backend-empty = { path = "../gfx/src/backend/empty" } -# gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan" } -# gfx-backend-gl = { path = "../gfx/src/backend/gl" } -# gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" } -# gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" } -# gfx-backend-metal = { path = "../gfx/src/backend/metal" } diff --git a/assets/voxygen/i18n/en/hud/hud_settings.ron b/assets/voxygen/i18n/en/hud/hud_settings.ron index 24023e2ba3..037e4868d1 100644 --- a/assets/voxygen/i18n/en/hud/hud_settings.ron +++ b/assets/voxygen/i18n/en/hud/hud_settings.ron @@ -10,7 +10,6 @@ "hud.settings.press_behavior.hold": "Hold", "hud.settings.help_window": "Help Window", "hud.settings.debug_info": "Debug Info", - "hud.settings.show_hitboxes": "Show hitboxes", "hud.settings.tips_on_startup": "Tips-On-Startup", "hud.settings.ui_scale": "UI-Scale", "hud.settings.relative_scaling": "Relative Scaling", @@ -58,10 +57,6 @@ "hud.settings.sprites_view_distance": "Sprites View Distance", "hud.settings.figures_view_distance": "Entities View Distance", "hud.settings.maximum_fps": "Maximum FPS", - "hud.settings.present_mode": "Present Mode", - "hud.settings.present_mode.fifo": "Fifo", - "hud.settings.present_mode.mailbox": "Mailbox", - "hud.settings.present_mode.immediate": "Immediate", "hud.settings.fov": "Field of View (deg)", "hud.settings.gamma": "Gamma", "hud.settings.exposure": "Exposure", @@ -81,7 +76,6 @@ "hud.settings.fullscreen_mode": "Fullscreen Mode", "hud.settings.fullscreen_mode.exclusive": "Exclusive", "hud.settings.fullscreen_mode.borderless": "Borderless", - "hud.settings.gpu_profiler": "Enable GPU timing (not supported everywhere)", "hud.settings.particles": "Particles", "hud.settings.lossy_terrain_compression": "Lossy terrain compression", "hud.settings.resolution": "Resolution", diff --git a/assets/voxygen/shaders/antialias/fxaa.glsl b/assets/voxygen/shaders/antialias/fxaa.glsl index 24ffe84cb9..c04d82bf02 100644 --- a/assets/voxygen/shaders/antialias/fxaa.glsl +++ b/assets/voxygen/shaders/antialias/fxaa.glsl @@ -1,3 +1,5 @@ +uniform sampler2D src_color; + const float FXAA_SCALE = 1.25; /** @@ -55,17 +57,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //optimized version for mobile, where dependent //texture reads can be a bottleneck -vec4 fxaa(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution, +vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution, vec2 v_rgbNW, vec2 v_rgbNE, vec2 v_rgbSW, vec2 v_rgbSE, vec2 v_rgbM) { vec4 color; mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); - vec3 rgbNW = texture(sampler2D(tex, smplr), v_rgbNW).xyz; - vec3 rgbNE = texture(sampler2D(tex, smplr), v_rgbNE).xyz; - vec3 rgbSW = texture(sampler2D(tex, smplr), v_rgbSW).xyz; - vec3 rgbSE = texture(sampler2D(tex, smplr), v_rgbSE).xyz; - vec4 texColor = texture(sampler2D(tex, smplr), v_rgbM); + vec3 rgbNW = texture(tex, v_rgbNW).xyz; + vec3 rgbNE = texture(tex, v_rgbNE).xyz; + vec3 rgbSW = texture(tex, v_rgbSW).xyz; + vec3 rgbSE = texture(tex, v_rgbSE).xyz; + vec4 texColor = texture(tex, v_rgbM); vec3 rgbM = texColor.xyz; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); @@ -89,11 +91,11 @@ vec4 fxaa(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution, dir * rcpDirMin)) * inverseVP; vec3 rgbA = 0.5 * ( - texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + - texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); + texture(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + + texture(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * ( - texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * -0.5).xyz + - texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * 0.5).xyz); + texture(tex, fragCoord * inverseVP + dir * -0.5).xyz + + texture(tex, fragCoord * inverseVP + dir * 0.5).xyz); float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) @@ -117,7 +119,7 @@ void texcoords(vec2 fragCoord, vec2 resolution, } -vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { +vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { mediump vec2 v_rgbNW; mediump vec2 v_rgbNE; mediump vec2 v_rgbSW; @@ -131,5 +133,5 @@ vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { texcoords(scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); //compute FXAA - return fxaa(tex, smplr, scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); + return fxaa(tex, scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); } diff --git a/assets/voxygen/shaders/antialias/msaa-x16.glsl b/assets/voxygen/shaders/antialias/msaa-x16.glsl index df5bbaaa2c..7a23b212a8 100644 --- a/assets/voxygen/shaders/antialias/msaa-x16.glsl +++ b/assets/voxygen/shaders/antialias/msaa-x16.glsl @@ -1,22 +1,24 @@ -vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); - vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0); - vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1); - vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2); - vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3); - vec4 sample5 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 4); - vec4 sample6 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 5); - vec4 sample7 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 6); - vec4 sample8 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 7); - vec4 sample9 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 8); - vec4 sample10 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 9); - vec4 sample11 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 10); - vec4 sample12 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 11); - vec4 sample13 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 12); - vec4 sample14 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 13); - vec4 sample15 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 14); - vec4 sample16 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 15); + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); + vec4 sample5 = texelFetch(tex, texel_coord, 4); + vec4 sample6 = texelFetch(tex, texel_coord, 5); + vec4 sample7 = texelFetch(tex, texel_coord, 6); + vec4 sample8 = texelFetch(tex, texel_coord, 7); + vec4 sample9 = texelFetch(tex, texel_coord, 8); + vec4 sample10 = texelFetch(tex, texel_coord, 9); + vec4 sample11 = texelFetch(tex, texel_coord, 10); + vec4 sample12 = texelFetch(tex, texel_coord, 11); + vec4 sample13 = texelFetch(tex, texel_coord, 12); + vec4 sample14 = texelFetch(tex, texel_coord, 13); + vec4 sample15 = texelFetch(tex, texel_coord, 14); + vec4 sample16 = texelFetch(tex, texel_coord, 15); // Average Samples vec4 msaa_color = ( @@ -25,4 +27,4 @@ vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { ) / 16.0; return msaa_color; -} +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/msaa-x4.glsl b/assets/voxygen/shaders/antialias/msaa-x4.glsl index f6d2e76841..8537f5eb4d 100644 --- a/assets/voxygen/shaders/antialias/msaa-x4.glsl +++ b/assets/voxygen/shaders/antialias/msaa-x4.glsl @@ -1,13 +1,15 @@ -vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); - vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0); - vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1); - vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2); - vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3); + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); // Average Samples vec4 msaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0; return msaa_color; -} +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/msaa-x8.glsl b/assets/voxygen/shaders/antialias/msaa-x8.glsl index cacdc7c4bf..d39d824639 100644 --- a/assets/voxygen/shaders/antialias/msaa-x8.glsl +++ b/assets/voxygen/shaders/antialias/msaa-x8.glsl @@ -1,17 +1,19 @@ -vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { +uniform sampler2DMS src_color; + +vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) { ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); - vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0); - vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1); - vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2); - vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3); - vec4 sample5 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 4); - vec4 sample6 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 5); - vec4 sample7 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 6); - vec4 sample8 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 7); + vec4 sample1 = texelFetch(tex, texel_coord, 0); + vec4 sample2 = texelFetch(tex, texel_coord, 1); + vec4 sample3 = texelFetch(tex, texel_coord, 2); + vec4 sample4 = texelFetch(tex, texel_coord, 3); + vec4 sample5 = texelFetch(tex, texel_coord, 4); + vec4 sample6 = texelFetch(tex, texel_coord, 5); + vec4 sample7 = texelFetch(tex, texel_coord, 6); + vec4 sample8 = texelFetch(tex, texel_coord, 7); // Average Samples vec4 msaa_color = (sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7 + sample8) / 8.0; return msaa_color; -} +} \ No newline at end of file diff --git a/assets/voxygen/shaders/antialias/none.glsl b/assets/voxygen/shaders/antialias/none.glsl index 2e537bc3f8..50c953b175 100644 --- a/assets/voxygen/shaders/antialias/none.glsl +++ b/assets/voxygen/shaders/antialias/none.glsl @@ -1,3 +1,5 @@ -vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) { - return texture(sampler2D(tex, smplr), fragCoord / resolution); -} +uniform sampler2D src_color; + +vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { + return texture(src_color, fragCoord / resolution); +} \ No newline at end of file diff --git a/assets/voxygen/shaders/blit-frag.glsl b/assets/voxygen/shaders/blit-frag.glsl deleted file mode 100644 index d0818b374a..0000000000 --- a/assets/voxygen/shaders/blit-frag.glsl +++ /dev/null @@ -1,16 +0,0 @@ -#version 420 core - -layout(set = 0, binding = 0) -uniform texture2D t_src_color; -layout(set = 0, binding = 1) -uniform sampler s_src_color; - -layout(location = 0) in vec2 uv; - -layout(location = 0) out vec4 tgt_color; - -void main() { - vec4 color = texture(sampler2D(t_src_color, s_src_color), uv); - - tgt_color = vec4(color.rgb, 1); -} diff --git a/assets/voxygen/shaders/blit-vert.glsl b/assets/voxygen/shaders/blit-vert.glsl deleted file mode 100644 index f6538bd743..0000000000 --- a/assets/voxygen/shaders/blit-vert.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version 420 core - -layout(location = 0) out vec2 uv; - -void main() { - // Generate fullscreen triangle - vec2 v_pos = vec2( - float(gl_VertexIndex / 2) * 4.0 - 1.0, - float(gl_VertexIndex % 2) * 4.0 - 1.0 - ); - - uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5; - - gl_Position = vec4(v_pos, 0.0, 1.0); -} diff --git a/assets/voxygen/shaders/clouds-frag.glsl b/assets/voxygen/shaders/clouds-frag.glsl index 7d02bf0f1d..082765829f 100644 --- a/assets/voxygen/shaders/clouds-frag.glsl +++ b/assets/voxygen/shaders/clouds-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -22,45 +22,46 @@ #include #include -layout(set = 1, binding = 0) -uniform texture2D t_src_color; -layout(set = 1, binding = 1) -uniform sampler s_src_color; +uniform sampler2D src_depth; -layout(set = 1, binding = 2) -uniform texture2D t_src_depth; -layout(set = 1, binding = 3) -uniform sampler s_src_depth; +in vec2 f_pos; -layout(location = 0) in vec2 uv; - -layout (std140, set = 1, binding = 4) +layout (std140) uniform u_locals { mat4 proj_mat_inv; mat4 view_mat_inv; }; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; +float depth_at(vec2 uv) { + float buf_depth = texture(src_depth, uv).x; + vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0); + vec4 view_space = proj_mat_inv * clip_space; + view_space /= view_space.w; + return -view_space.z; +} vec3 wpos_at(vec2 uv) { - float buf_depth = texture(sampler2D(t_src_depth, s_src_depth), uv).x; + float buf_depth = texture(src_depth, uv).x * 2.0 - 1.0; mat4 inv = view_mat_inv * proj_mat_inv;//inverse(all_mat); - vec4 clip_space = vec4((uv * 2.0 - 1.0) * vec2(1, -1), buf_depth, 1.0); + vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0); vec4 view_space = inv * clip_space; view_space /= view_space.w; - if (buf_depth == 0.0) { + if (buf_depth == 1.0) { vec3 direction = normalize(view_space.xyz); - return direction.xyz * 524288.0625 + cam_pos.xyz; + return direction.xyz * 100000.0 + cam_pos.xyz; } else { return view_space.xyz; } } void main() { - vec4 color = texture(sampler2D(t_src_color, s_src_color), uv); + vec2 uv = (f_pos + 1.0) * 0.5; - // Apply clouds + vec4 color = texture(src_color, uv); + + // Apply clouds to `aa_color` #if (CLOUD_MODE != CLOUD_MODE_NONE) vec3 wpos = wpos_at(uv); float dist = distance(wpos, cam_pos.xyz); diff --git a/assets/voxygen/shaders/clouds-vert.glsl b/assets/voxygen/shaders/clouds-vert.glsl index 933d3a3dc3..35b786997f 100644 --- a/assets/voxygen/shaders/clouds-vert.glsl +++ b/assets/voxygen/shaders/clouds-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -18,17 +18,12 @@ #include -layout(location = 0) out vec2 uv; +in vec2 v_pos; + +out vec2 f_pos; void main() { - // Generate fullscreen triangle - vec2 v_pos = vec2( - float(gl_VertexIndex / 2) * 4.0 - 1.0, - float(gl_VertexIndex % 2) * 4.0 - 1.0 - ); + f_pos = v_pos; - // Flip y and transform into 0.0 to 1.0 range - uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5; - - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } diff --git a/assets/voxygen/shaders/debug-frag.glsl b/assets/voxygen/shaders/debug-frag.glsl deleted file mode 100644 index c681b64bfc..0000000000 --- a/assets/voxygen/shaders/debug-frag.glsl +++ /dev/null @@ -1,19 +0,0 @@ -#version 420 core - -#include - -layout (location = 0) -in vec4 f_color; - -layout (std140, set = 1, binding = 0) -uniform u_locals { - vec4 w_pos; - vec4 w_color; -}; - -layout (location = 0) -out vec4 tgt_color; - -void main() { - tgt_color = f_color; -} diff --git a/assets/voxygen/shaders/debug-vert.glsl b/assets/voxygen/shaders/debug-vert.glsl deleted file mode 100644 index 97d774a642..0000000000 --- a/assets/voxygen/shaders/debug-vert.glsl +++ /dev/null @@ -1,20 +0,0 @@ -#version 420 core - -#include - -layout (location = 0) -in vec3 v_pos; - -layout (std140, set = 1, binding = 0) -uniform u_locals { - vec4 w_pos; - vec4 w_color; -}; - -layout (location = 0) -out vec4 f_color; - -void main() { - f_color = w_color; - gl_Position = all_mat * vec4((v_pos + w_pos.xyz) - focus_off.xyz, 1); -} diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 2685fb61a6..0bfb9a5cad 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #define FIGURE_SHADER @@ -17,17 +17,14 @@ #define HAS_SHADOW_MAPS #include -#include -#include -#include -layout(location = 0) in vec3 f_pos; +in vec3 f_pos; // in float dummy; // in vec3 f_col; // in float f_ao; // flat in uint f_pos_norm; -layout(location = 1) flat in vec3 f_norm; -/*centroid */layout(location = 2) in vec2 f_uv_pos; +flat in vec3 f_norm; +/*centroid */in vec2 f_uv_pos; // in float f_alt; // in vec4 f_shadow; // in vec3 light_pos[2]; @@ -38,10 +35,7 @@ layout(location = 1) flat in vec3 f_norm; // const vec4 sun_pos = vec4(0.0); // #endif -layout(set = 3, binding = 0) -uniform texture2D t_col_light; -layout(set = 3, binding = 1) -uniform sampler s_col_light; +uniform sampler2D t_col_light; //struct ShadowLocals { // mat4 shadowMatrices; @@ -53,7 +47,7 @@ uniform sampler s_col_light; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; //}; -layout (std140, set = 2, binding = 0) +layout (std140) uniform u_locals { mat4 model_mat; vec4 highlight_col; @@ -71,12 +65,16 @@ struct BoneData { mat4 normals_mat; }; -layout (std140, set = 2, binding = 1) +layout (std140) uniform u_bones { BoneData bones[16]; }; -layout(location = 0) out vec4 tgt_color; +#include +#include +#include + +out vec4 tgt_color; void main() { // vec2 texSize = textureSize(t_col_light, 0); @@ -90,7 +88,8 @@ void main() { float f_ao, f_glow; uint material = 0xFFu; - vec3 f_col = greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_ao, f_glow, material); + vec3 f_col = greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_ao, f_glow, material); + // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // vec3 my_chunk_pos = (vec3((uvec3(f_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; @@ -132,7 +131,7 @@ void main() { #endif #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); diff --git a/assets/voxygen/shaders/figure-vert.glsl b/assets/voxygen/shaders/figure-vert.glsl index 931cb09984..021b319a11 100644 --- a/assets/voxygen/shaders/figure-vert.glsl +++ b/assets/voxygen/shaders/figure-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -17,15 +17,15 @@ #include #include -layout(location = 0) in uint v_pos_norm; -layout(location = 1) in uint v_atlas_pos; +in uint v_pos_norm; +in uint v_atlas_pos; // in vec3 v_norm; /* in uint v_col; // out vec3 light_pos[2]; in uint v_ao_bone; */ -layout (std140, set = 2, binding = 0) +layout (std140) uniform u_locals { mat4 model_mat; vec4 highlight_col; @@ -40,16 +40,10 @@ uniform u_locals { struct BoneData { mat4 bone_mat; - // This is actually a matrix, but we explicitly rely on being able to index into it - // in column major order, and some shader compilers seem to transpose the matrix to - // a different format when it's copied out of the array. So we shouldn't put it in - // a local variable (I think explicitly marking it as a vec4[4] works, but I'm not - // sure whether it optimizes the same, and in any case the fact that there's a - // format change suggests an actual wasteful copy is happening). mat4 normals_mat; }; -layout (std140, set = 2, binding = 1) +layout (std140) uniform u_bones { // Warning: might not actually be 16 elements long. Don't index out of bounds! BoneData bones[16]; @@ -65,11 +59,11 @@ uniform u_bones { // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; //}; -layout(location = 0) out vec3 f_pos; +out vec3 f_pos; // flat out uint f_pos_norm; -layout(location = 1) flat out vec3 f_norm; +flat out vec3 f_norm; // float dummy; -/*centroid */layout(location = 2) out vec2 f_uv_pos; +/*centroid */out vec2 f_uv_pos; // out vec3 f_col; // out float f_ao; // out float f_alt; @@ -84,14 +78,16 @@ void main() { /* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */ uint bone_idx = (v_pos_norm >> 27) & 0xFu; - // mat4 combined_mat = model_mat * bone_mat; + mat4 bone_mat = bones[bone_idx].bone_mat; + mat4 normals_mat = bones[bone_idx].normals_mat; + mat4 combined_mat = /*model_mat * */bone_mat; vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; // vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1); f_pos = ( - bones[bone_idx].bone_mat * + combined_mat * vec4(pos, 1.0) ).xyz + (model_pos - focus_off.xyz); @@ -114,7 +110,7 @@ void main() { // vec3 norm = normals[normal_idx]; uint axis_idx = v_atlas_pos & 3u; - vec3 norm = bones[bone_idx].normals_mat[axis_idx].xyz; + vec3 norm = normals_mat[axis_idx].xyz; // norm = normalize(norm); // vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); diff --git a/assets/voxygen/shaders/fluid-frag/cheap.glsl b/assets/voxygen/shaders/fluid-frag/cheap.glsl index f405fdd039..91828bf179 100644 --- a/assets/voxygen/shaders/fluid-frag/cheap.glsl +++ b/assets/voxygen/shaders/fluid-frag/cheap.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -21,8 +21,8 @@ #include #include -layout(location = 0) in vec3 f_pos; -layout(location = 1) flat in uint f_pos_norm; +in vec3 f_pos; +flat in uint f_pos_norm; // in vec3 f_col; // in float f_light; // in vec3 light_pos[2]; @@ -37,14 +37,16 @@ layout(location = 1) flat in uint f_pos_norm; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // }; -layout(std140, set = 2, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; ivec4 atlas_offs; }; -layout(location = 0) out vec4 tgt_color; +uniform sampler2D t_waves; + +out vec4 tgt_color; #include #include @@ -90,7 +92,7 @@ void main() { #endif #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 019e2b0618..1b409c2a53 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -23,8 +23,8 @@ #include #include -layout(location = 0) in vec3 f_pos; -layout(location = 1) flat in uint f_pos_norm; +in vec3 f_pos; +flat in uint f_pos_norm; // in vec3 f_col; // in float f_light; // in vec3 light_pos[2]; @@ -39,14 +39,16 @@ layout(location = 1) flat in uint f_pos_norm; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; //}; -layout(std140, set = 2, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; ivec4 atlas_offs; }; -layout(location = 0) out vec4 tgt_color; +uniform sampler2D t_waves; + +out vec4 tgt_color; #include #include @@ -63,25 +65,25 @@ float wave_height(vec3 pos) { pos *= 0.5; vec3 big_warp = ( - texture(sampler2D(t_noise, s_noise), fract(pos.xy * 0.03 + timer * 0.01)).xyz * 0.5 + - texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.03 - timer * 0.01)).xyz * 0.5 + + texture(t_noise, fract(pos.xy * 0.03 + timer * 0.01)).xyz * 0.5 + + texture(t_noise, fract(pos.yx * 0.03 - timer * 0.01)).xyz * 0.5 + vec3(0) ); vec3 warp = ( - texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.1 + timer * 0.02)).xyz * 0.3 + - texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.1 - timer * 0.02)).xyz * 0.3 + + texture(t_noise, fract(pos.yx * 0.1 + timer * 0.02)).xyz * 0.3 + + texture(t_noise, fract(pos.yx * 0.1 - timer * 0.02)).xyz * 0.3 + vec3(0) ); float height = ( - (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.03 + big_warp.xy + timer * 0.05).y - 0.5) * 1.0 + - (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.03 + big_warp.yx - timer * 0.05).y - 0.5) * 1.0 + - (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.1 + warp.xy + timer * 0.1).x - 0.5) * 0.5 + - (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.1 + warp.yx - timer * 0.1).x - 0.5) * 0.5 + - (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.3 + warp.xy * 0.5 + timer * 0.1).x - 0.5) * 0.2 + - (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.3 + warp.yx * 0.5 - timer * 0.1).x - 0.5) * 0.2 + - (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 1.0 + warp.yx * 0.0 - timer * 0.1).x - 0.5) * 0.05 + + (texture(t_noise, (pos.xy + pos.z) * 0.03 + big_warp.xy + timer * 0.05).y - 0.5) * 1.0 + + (texture(t_noise, (pos.yx + pos.z) * 0.03 + big_warp.yx - timer * 0.05).y - 0.5) * 1.0 + + (texture(t_noise, (pos.xy + pos.z) * 0.1 + warp.xy + timer * 0.1).x - 0.5) * 0.5 + + (texture(t_noise, (pos.yx + pos.z) * 0.1 + warp.yx - timer * 0.1).x - 0.5) * 0.5 + + (texture(t_noise, (pos.yx + pos.z) * 0.3 + warp.xy * 0.5 + timer * 0.1).x - 0.5) * 0.2 + + (texture(t_noise, (pos.xy + pos.z) * 0.3 + warp.yx * 0.5 - timer * 0.1).x - 0.5) * 0.2 + + (texture(t_noise, (pos.yx + pos.z) * 1.0 + warp.yx * 0.0 - timer * 0.1).x - 0.5) * 0.05 + 0.0 ); @@ -189,7 +191,7 @@ void main() { /* vec3 sun_dir = get_sun_dir(time_of_day.x); vec3 moon_dir = get_moon_dir(time_of_day.x); */ #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); diff --git a/assets/voxygen/shaders/fluid-vert.glsl b/assets/voxygen/shaders/fluid-vert.glsl index 0d68559912..6802ab11b9 100644 --- a/assets/voxygen/shaders/fluid-vert.glsl +++ b/assets/voxygen/shaders/fluid-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -20,10 +20,10 @@ #include #include -layout(location = 0) in uint v_pos_norm; +in uint v_pos_norm; // in uint v_col_light; -layout(std140, set = 2, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; @@ -40,8 +40,8 @@ uniform u_locals { // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // }; -layout(location = 0) out vec3 f_pos; -layout(location = 1) flat out uint f_pos_norm; +out vec3 f_pos; +flat out uint f_pos_norm; // out vec3 f_col; // out float f_light; // out vec3 light_pos[2]; diff --git a/assets/voxygen/shaders/include/cloud/regular.glsl b/assets/voxygen/shaders/include/cloud/regular.glsl index c31e3beca3..3e11d10810 100644 --- a/assets/voxygen/shaders/include/cloud/regular.glsl +++ b/assets/voxygen/shaders/include/cloud/regular.glsl @@ -34,7 +34,7 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) { // Mist sits close to the ground in valleys (TODO: use base_alt to put it closer to water) float mist_min_alt = 0.5; #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM) - mist_min_alt = (textureLod(sampler2D(t_noise, s_noise), pos.xy / 50000.0, 0).x - 0.5) * 1.5 + 0.5; + mist_min_alt = (texture(t_noise, pos.xy / 50000.0).x - 0.5) * 1.5 + 0.5; #endif mist_min_alt = view_distance.z * 1.5 * (1.0 + mist_min_alt * 0.5) + alt * 0.5 + 250; const float MIST_FADE_HEIGHT = 1000; @@ -146,9 +146,9 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) { #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM) emission_alt += (noise_3d(vec3(wind_pos.xy * 0.0005 + cloud_tendency * 0.2, emission_alt * 0.0001 + time_of_day.x * 0.001)) - 0.5) * 1000; #endif - float tail = (textureLod(sampler2D(t_noise, s_noise), wind_pos.xy * 0.00005, 0).x - 0.5) * 4 + (pos.z - emission_alt) * 0.0001; + float tail = (texture(t_noise, wind_pos.xy * 0.00005).x - 0.5) * 4 + (pos.z - emission_alt) * 0.0001; vec3 emission_col = vec3(0.8 + tail * 1.5, 0.5 - tail * 0.2, 0.3 + tail * 0.2); - float emission_nz = max(pow(textureLod(sampler2D(t_noise, s_noise), wind_pos.xy * 0.000015, 0).x, 8), 0.01) * 0.25 / (10.0 + abs(pos.z - emission_alt) / 80); + float emission_nz = max(pow(texture(t_noise, wind_pos.xy * 0.000015).x, 8), 0.01) * 0.25 / (10.0 + abs(pos.z - emission_alt) / 80); emission = emission_col * emission_nz * emission_strength * max(sun_dir.z, 0) * 500000 / (1000.0 + abs(pos.z - emission_alt) * 0.1); } @@ -196,7 +196,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of // improves visual quality for low cloud settings float splay = 1.0; #if (CLOUD_MODE == CLOUD_MODE_MINIMAL) - splay += (textureLod(sampler2D(t_noise, s_noise), vec2(atan2(dir.x, dir.y) * 2 / PI, dir.z) * 5.0 - time_of_day * 0.00005, 0).x - 0.5) * 0.025 / (1.0 + pow(dir.z, 2) * 10); + splay += (texture(t_noise, vec2(atan2(dir.x, dir.y) * 2 / PI, dir.z) * 5.0 - time_of_day * 0.00005).x - 0.5) * 0.025 / (1.0 + pow(dir.z, 2) * 10); #endif /* const float RAYLEIGH = 0.25; */ @@ -218,13 +218,12 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality); vec3 emission; - // `sample` is a reserved keyword - vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission); + vec4 sample = cloud_at(origin + dir * ldist * splay, ldist, emission); - vec2 density_integrals = max(sample_.zw, vec2(0)); + vec2 density_integrals = max(sample.zw, vec2(0)); - float sun_access = max(sample_.x, 0); - float moon_access = max(sample_.y, 0); + float sun_access = max(sample.x, 0); + float moon_access = max(sample.y, 0); float cloud_scatter_factor = density_integrals.x; float global_scatter_factor = density_integrals.y; diff --git a/assets/voxygen/shaders/include/globals.glsl b/assets/voxygen/shaders/include/globals.glsl index cc75645b39..6b3c8373be 100644 --- a/assets/voxygen/shaders/include/globals.glsl +++ b/assets/voxygen/shaders/include/globals.glsl @@ -1,7 +1,5 @@ -#ifndef GLOBALS_GLSL -#define GLOBALS_GLSL - -layout(std140, set = 0, binding = 0) uniform u_globals { +layout (std140) +uniform u_globals { mat4 view_mat; mat4 proj_mat; mat4 all_mat; @@ -24,7 +22,6 @@ layout(std140, set = 0, binding = 0) uniform u_globals { // 1 - ThirdPerson uint cam_mode; float sprite_render_distance; - float globals_dummy; // Fix alignment. }; // Specifies the pattern used in the player dithering @@ -36,5 +33,3 @@ mat4 threshold_matrix = mat4( ); float distance_divider = 2; float shadow_dithering = 0.5; - -#endif diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index 2567109c10..8c64bd037d 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -7,17 +7,16 @@ struct Light { // mat4 light_proj; }; -layout (std140, set = 0, binding = 3) +layout (std140) uniform u_lights { - // TODO: insert light max count constant here when loading the shaders - Light lights[20]; + Light lights[31]; }; struct Shadow { vec4 shadow_pos_radius; }; -layout (std140, set = 0, binding = 4) +layout (std140) uniform u_shadows { Shadow shadows[24]; }; diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl index 74f73849cf..2644c2a4fe 100644 --- a/assets/voxygen/shaders/include/lod.glsl +++ b/assets/voxygen/shaders/include/lod.glsl @@ -1,20 +1,15 @@ -#ifndef LOD_GLSL -#define LOD_GLSL - #include #include #include -layout(set = 0, binding = 5) uniform texture2D t_alt; -layout(set = 0, binding = 6) uniform sampler s_alt; -layout(set = 0, binding = 7) uniform texture2D t_horizon; -layout(set = 0, binding = 8) uniform sampler s_horizon; +uniform sampler2D t_alt; +uniform sampler2D t_horizon; const float MIN_SHADOW = 0.33; -vec2 pos_to_uv(texture2D tex, sampler s, vec2 pos) { +vec2 pos_to_uv(sampler2D sampler, vec2 pos) { // Want: (pixel + 0.5) / W - vec2 texSize = textureSize(sampler2D(tex, s), 0); + vec2 texSize = textureSize(sampler, 0); vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize); return vec2(uv_pos.x, /*1.0 - */uv_pos.y); } @@ -37,9 +32,8 @@ vec4 cubic(float v) { } // NOTE: We assume the sampled coordinates are already in "texture pixels". -vec4 textureBicubic(texture2D tex, sampler sampl, vec2 texCoords) { - // TODO: remove all textureSize calls and replace with constants - vec2 texSize = textureSize(sampler2D(tex, sampl), 0); +vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { + vec2 texSize = textureSize(sampler, 0); vec2 invTexSize = 1.0 / texSize; /* texCoords.y = texSize.y - texCoords.y; */ @@ -62,57 +56,10 @@ vec4 textureBicubic(texture2D tex, sampler sampl, vec2 texCoords) { /* // Correct for map rotaton. offset.zw = 1.0 - offset.zw; */ - vec4 sample0 = texture(sampler2D(tex, sampl), offset.xz); - vec4 sample1 = texture(sampler2D(tex, sampl), offset.yz); - vec4 sample2 = texture(sampler2D(tex, sampl), offset.xw); - vec4 sample3 = texture(sampler2D(tex, sampl), offset.yw); - // vec4 sample0 = texelFetch(sampler, offset.xz, 0); - // vec4 sample1 = texelFetch(sampler, offset.yz, 0); - // vec4 sample2 = texelFetch(sampler, offset.xw, 0); - // vec4 sample3 = texelFetch(sampler, offset.yw, 0); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix( - mix(sample3, sample2, sx), mix(sample1, sample0, sx) - , sy); -} - -// 16 bit version (each of the 2 8-bit components are combined after bilinear sampling) -// NOTE: We assume the sampled coordinates are already in "texture pixels". -vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) { - vec2 texSize = textureSize(sampler2D(tex, sampl), 0); - vec2 invTexSize = 1.0 / texSize; - /* texCoords.y = texSize.y - texCoords.y; */ - - texCoords = texCoords/* * texSize */ - 0.5; - - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy; - // vec4 c = texCoords.xxyy + vec2 (-1, +1).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - /* // Correct for map rotaton. - offset.zw = 1.0 - offset.zw; */ - - vec4 sample0_v4 = texture(sampler2D(tex, sampl), offset.xz); - vec4 sample1_v4 = texture(sampler2D(tex, sampl), offset.yz); - vec4 sample2_v4 = texture(sampler2D(tex, sampl), offset.xw); - vec4 sample3_v4 = texture(sampler2D(tex, sampl), offset.yw); - vec2 sample0 = sample0_v4.rb / 256.0 + sample0_v4.ga; - vec2 sample1 = sample1_v4.rb / 256.0 + sample1_v4.ga; - vec2 sample2 = sample2_v4.rb / 256.0 + sample2_v4.ga; - vec2 sample3 = sample3_v4.rb / 256.0 + sample3_v4.ga; + vec4 sample0 = texture(sampler, offset.xz); + vec4 sample1 = texture(sampler, offset.yz); + vec4 sample2 = texture(sampler, offset.xw); + vec4 sample3 = texture(sampler, offset.yw); // vec4 sample0 = texelFetch(sampler, offset.xz, 0); // vec4 sample1 = texelFetch(sampler, offset.yz, 0); // vec4 sample2 = texelFetch(sampler, offset.xw, 0); @@ -127,9 +74,8 @@ vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) { } float alt_at(vec2 pos) { - vec4 alt_sample = textureLod/*textureBicubic16*/(sampler2D(t_alt, s_alt), pos_to_uv(t_alt, s_alt, pos), 0); - return (/*round*/((alt_sample.r / 256.0 + alt_sample.g) * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); - //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; + return (/*round*/(texture/*textureBicubic*/(t_alt, pos_to_uv(t_alt, pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); + //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; // return 0.0 // + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0 @@ -142,7 +88,7 @@ float alt_at_real(vec2 pos) { // #if (FLUID_MODE == FLUID_MODE_CHEAP) // return alt_at(pos); // #elif (FLUID_MODE == FLUID_MODE_SHINY) - return (/*round*/(textureBicubic16(t_alt, s_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); + return (/*round*/(textureBicubic(t_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); // #endif //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; @@ -258,7 +204,7 @@ vec2 splay(vec2 pos) { const float SQRT_2 = sqrt(2.0) / 2.0; // /const float CBRT_2 = cbrt(2.0) / 2.0; // vec2 splayed = pos * (view_distance.x * SQRT_2 + pow(len * 0.5, 3.0) * (SPLAY_MULT - view_distance.x)); - vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(sampler2D(t_alt, s_alt), 0) * 32.0/* - view_distance.x*/)); + vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(t_alt, 0) * 32.0/* - view_distance.x*/)); if (abs(pos.x) > 0.99 || abs(pos.y) > 0.99) { splayed *= 10.0; } @@ -334,18 +280,13 @@ vec3 lod_pos(vec2 pos, vec2 focus_pos) { } #ifdef HAS_LOD_FULL_INFO -layout(set = 0, binding = 10) -uniform texture2D t_map; -layout(set = 0, binding = 11) -uniform sampler s_map; +uniform sampler2D t_map; vec3 lod_col(vec2 pos) { //return vec3(0, 0.5, 0); // return /*linear_to_srgb*/vec3(alt_at(pos), textureBicubic(t_map, pos_to_tex(pos)).gb); - return /*linear_to_srgb*/(textureBicubic(t_map, s_map, pos_to_tex(pos)).rgb) + return /*linear_to_srgb*/(textureBicubic(t_map, pos_to_tex(pos)).rgb) ;//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; //+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; } #endif - -#endif diff --git a/assets/voxygen/shaders/include/random.glsl b/assets/voxygen/shaders/include/random.glsl index 2b6347b71c..6f337fee35 100644 --- a/assets/voxygen/shaders/include/random.glsl +++ b/assets/voxygen/shaders/include/random.glsl @@ -1,8 +1,4 @@ -#ifndef RANDOM_GLSL -#define RANDOM_GLSL - -layout(set = 0, binding = 1) uniform texture2D t_noise; -layout(set = 0, binding = 2) uniform sampler s_noise; +uniform sampler2D t_noise; float hash(vec4 p) { p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121); @@ -32,7 +28,7 @@ float hash_fast(uvec3 q) // 2D, but using shifted 2D textures float noise_2d(vec2 pos) { - return textureLod(sampler2D(t_noise, s_noise), pos, 0).x; + return texture(t_noise, pos).x; } // 3D, but using shifted 2D textures @@ -41,7 +37,7 @@ float noise_3d(vec3 pos) { uint z = uint(trunc(pos.z)); vec2 offs0 = vec2(hash_one(z), hash_one(z + 73u)); vec2 offs1 = vec2(hash_one(z + 1u), hash_one(z + 1u + 73u)); - return mix(textureLod(sampler2D(t_noise, s_noise), pos.xy + offs0, 0).x, textureLod(sampler2D(t_noise, s_noise), pos.xy + offs1, 0).x, fract(pos.z)); + return mix(texture(t_noise, pos.xy + offs0).x, texture(t_noise, pos.xy + offs1).x, fract(pos.z)); } // 3D version of `snoise` @@ -104,4 +100,3 @@ vec3 smooth_rand(vec3 pos, float lerp_axis) { vec3 r1 = rand_perm_3(vec3(pos.x, pos.y, pos.z) + floor(lerp_axis + 1.0)); return r0 + (r1 - r0) * fract(lerp_axis); } -#endif diff --git a/assets/voxygen/shaders/include/shadows.glsl b/assets/voxygen/shaders/include/shadows.glsl index bd65dd3b5c..3f4b4a0ceb 100644 --- a/assets/voxygen/shaders/include/shadows.glsl +++ b/assets/voxygen/shaders/include/shadows.glsl @@ -1,29 +1,22 @@ -#ifndef SHADOWS_GLSL -#define SHADOWS_GLSL - #ifdef HAS_SHADOW_MAPS -#if (SHADOW_MODE == SHADOW_MODE_MAP) -layout (std140, set = 0, binding = 9) -uniform u_light_shadows { + #if (SHADOW_MODE == SHADOW_MODE_MAP) +struct ShadowLocals { mat4 shadowMatrices; mat4 texture_mat; }; -// Use with sampler2DShadow -layout(set = 1, binding = 2) -uniform texture2D t_directed_shadow_maps; -layout(set = 1, binding = 3) -uniform samplerShadow s_directed_shadow_maps; +layout (std140) +uniform u_light_shadows { + ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +}; + +uniform sampler2DShadow t_directed_shadow_maps; // uniform sampler2DArrayShadow t_directed_shadow_maps; // uniform samplerCubeArrayShadow t_shadow_maps; // uniform samplerCubeArray t_shadow_maps; -// Use with samplerCubeShadow -layout(set = 1, binding = 0) -uniform textureCube t_point_shadow_maps; -layout(set = 1, binding = 1) -uniform samplerShadow s_point_shadow_maps; +uniform samplerCubeShadow t_point_shadow_maps; // uniform samplerCube t_shadow_maps; // uniform sampler2DArray t_directed_shadow_maps; @@ -42,13 +35,9 @@ float VectorToDepth (vec3 Vec) // float NormZComp = (screen_res.w+screen_res.z) / (screen_res.w-screen_res.z) - (2*screen_res.w*screen_res.z)/(screen_res.w-screen_res.z)/LocalZcomp; // float NormZComp = 1.0 - shadow_proj_factors.y / shadow_proj_factors.x / LocalZcomp; - // -(1 + 2n/(f-n)) - 2(1 + n/(f-n)) * n/z - // -(1 + n/(f-n)) - (1 + n/(f-n)) * n/z - // f/(f-n) - fn/(f-n)/z float NormZComp = shadow_proj_factors.x - shadow_proj_factors.y / LocalZcomp; // NormZComp = -1000.0 / (NormZComp + 10000.0); - // return (NormZComp + 1.0) * 0.5; - return NormZComp; + return (NormZComp + 1.0) * 0.5; // float NormZComp = length(LocalZcomp); // NormZComp = -NormZComp / screen_res.w; @@ -75,9 +64,7 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, / { float currentDepth = VectorToDepth(fragToLight);// + bias; - // currentDepth = -currentDepth * 0.5 + 0.5; - - float visibility = texture(samplerCubeShadow(t_point_shadow_maps, s_point_shadow_maps), vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); /* if (visibility == 1.0 || visibility == 0.0) { return visibility; } */ @@ -167,45 +154,10 @@ float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_po } */ // vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz; // sun_pos.z += sun_pos.w * bias; - vec4 sun_pos = texture_mat/*shadowMatrices*/ * vec4(fragPos, 1.0); - // sun_pos.xy = 0.5 * sun_pos.w + sun_pos.xy * 0.5; - // sun_pos.xy = sun_pos.ww - sun_pos.xy; - // sun_pos.xyz /= abs(sun_pos.w); - // sun_pos.w = sign(sun_pos.w); - // sun_pos.xy = (sun_pos.xy + 1.0) * 0.5; - // vec4 orig_pos = warpViewMat * lightViewMat * vec4(fragPos, 1.0); - // - // vec4 shadow_pos; - // shadow_pos.xyz = (warpProjMat * orig_pos).xyz: - // shadow_pos.w = orig_pos.y; - // - // sun_pos.xy = 0.5 * (shadow_pos.xy + shadow_pos.w) = 0.5 * (shadow_pos.xy + orig_pos.yy); - // sun_pos.z = shadow_pos.z; - // - // sun_pos.w = sign(shadow_pos.w) = sign(orig_pos.y); - // sun_pos.xyz = sun_pos.xyz / shadow_pos.w = vec3(0.5 * shadow_pos.xy / orig_pos.yy + 0.5, shadow_pos.z / orig_pos.y) - // = vec3(0.5 * (2.0 * warp_pos.xy / orig_pos.yy - (max_warp_pos + min_warp_pos).xy) / (max_warp_pos - min_warp_pos).xy + 0.5, - // -(warp_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // = vec3((warp_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x, - // (warp_pos.y / orig_pos.y - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y, - // -(warp_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x, - // (((far+near) - 2.0 * near * far / orig_pos.y)/(far-near) - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y, - // -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x, - // (2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0 - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y, - // -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x, - // (2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0 - 0.0) / (1.0 - 0.0), - // -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x, - // 2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0, - // -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z ) - // - // orig_pos.y = n: warp_pos.y = 2*(1-f/n)*n/(f-n) + 1 = 2*(n-f)/(f-n) + 1 = 2 * -1 + 1 = -1, sun_pos.y = (-1 - -1) / 2 = 0 - // orig_pos.y = f: warp_pos.y = 2*(1-f/f)*n/(f-n) + 1 = 2*(1-1)*n/(f-n) + 1 = 2 * 0 * n/(f-n) + 1 = 1, sun_pos.y = (1 - -1) / 2 = 1 - // - float visibility = textureProj(sampler2DShadow(t_directed_shadow_maps, s_directed_shadow_maps), sun_pos); + mat4 texture_mat = shadowMats[0].texture_mat; + vec4 sun_pos = texture_mat * vec4(fragPos, 1.0); + // sun_pos.z -= sun_pos.w * bias; + float visibility = textureProj(t_directed_shadow_maps, sun_pos); /* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0)); float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */ // float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos)); @@ -264,5 +216,3 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, / return 1.0; } #endif - -#endif diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index b44c95d522..41ac27e511 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -1,6 +1,3 @@ -#ifndef SKY_GLSL -#define SKY_GLSL - #include #include #include @@ -87,7 +84,7 @@ vec2 wind_offset = vec2(time_of_day.x * wind_speed); float cloud_scale = view_distance.z / 150.0; float cloud_tendency_at(vec2 pos) { - float nz = textureLod(sampler2D(t_noise, s_noise), (pos + wind_offset) / 60000.0 / cloud_scale, 0).x - 0.3; + float nz = texture(t_noise, (pos + wind_offset) / 60000.0 / cloud_scale).x - 0.3; nz = pow(clamp(nz, 0, 1), 3); return nz; } @@ -428,7 +425,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) { mix( SKY_DUSK_TOP, SKY_NIGHT_TOP, - pow(max(sun_dir.z, 0.0), 0.2) + max(pow(sun_dir.z, 0.2), 0) ) + star, SKY_DAY_TOP, max(-sun_dir.z, 0) @@ -437,7 +434,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) { vec3 sky_mid = mix( mix( SKY_DUSK_MID, SKY_NIGHT_MID, - pow(max(sun_dir.z, 0.0), 0.1) + max(pow(sun_dir.z, 0.1), 0) ), SKY_DAY_MID, max(-sun_dir.z, 0) @@ -447,7 +444,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) { mix( SKY_DUSK_BOT, SKY_NIGHT_BOT, - pow(max(sun_dir.z, 0.0), 0.2) + max(pow(sun_dir.z, 0.2), 0) ), SKY_DAY_BOT, max(-sun_dir.z, 0) @@ -643,5 +640,3 @@ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted // float sum_col = color.r + color.g + color.b; // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); } - -#endif diff --git a/assets/voxygen/shaders/include/srgb.glsl b/assets/voxygen/shaders/include/srgb.glsl index 118221a137..c978b4febb 100644 --- a/assets/voxygen/shaders/include/srgb.glsl +++ b/assets/voxygen/shaders/include/srgb.glsl @@ -1,5 +1,3 @@ -#ifndef SRGB_GLSL -#define SRGB_GLSL // Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); @@ -620,8 +618,8 @@ vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_a //} //#endif -vec3 greedy_extract_col_light_attr(texture2D t_col_light, sampler s_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out uint f_attr) { - uvec4 f_col_light = uvec4(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos), 0) * 255); +vec3 greedy_extract_col_light_attr(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out uint f_attr) { + uvec4 f_col_light = uvec4(texelFetch(t_col_light, ivec2(f_uv_pos), 0) * 255); vec3 f_col = vec3( float(((f_col_light.r & 0x7u) << 1u) | (f_col_light.b & 0xF0u)), float(f_col_light.a), @@ -630,9 +628,9 @@ vec3 greedy_extract_col_light_attr(texture2D t_col_light, sampler s_col_light, v // TODO: Figure out how to use `texture` and modulation to avoid needing to do manual filtering vec2 light_00 = vec2(uvec2(f_col_light.rg) >> 3u); - vec2 light_10 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(1, 0), 0).rg * 255.0) >> 3u); - vec2 light_01 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(0, 1), 0).rg * 255.0) >> 3u); - vec2 light_11 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(1, 1), 0).rg * 255.0) >> 3u); + vec2 light_10 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(1, 0), 0).rg * 255.0) >> 3u); + vec2 light_01 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(0, 1), 0).rg * 255.0) >> 3u); + vec2 light_11 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(1, 1), 0).rg * 255.0) >> 3u); vec2 light_0 = mix(light_00, light_01, fract(f_uv_pos.y)); vec2 light_1 = mix(light_10, light_11, fract(f_uv_pos.y)); vec2 light = mix(light_0, light_1, fract(f_uv_pos.x)); @@ -646,8 +644,7 @@ vec3 greedy_extract_col_light_attr(texture2D t_col_light, sampler s_col_light, v return srgb_to_linear(f_col); } -vec3 greedy_extract_col_light_glow(texture2D t_col_light, sampler s_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) { +vec3 greedy_extract_col_light_glow(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) { uint f_attr; - return greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_light, f_glow, f_attr); + return greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_light, f_glow, f_attr); } -#endif diff --git a/assets/voxygen/shaders/light-shadows-directed-frag.glsl b/assets/voxygen/shaders/light-shadows-directed-frag.glsl new file mode 100644 index 0000000000..e201b6ed5e --- /dev/null +++ b/assets/voxygen/shaders/light-shadows-directed-frag.glsl @@ -0,0 +1,48 @@ +// NOTE: We currently do nothing, and just rely on the default shader behavior. +// +// However, in the future we might apply some depth transforms here. + +#version 330 core +// #extension ARB_texture_storage : enable + +#include + +#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION + +#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY + +#if (FLUID_MODE == FLUID_MODE_CHEAP) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE +#elif (FLUID_MODE == FLUID_MODE_SHINY) +#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE +#endif + +#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET + +#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN + +// // Currently, we only need globals for the far plane. +// #include +// // Currently, we only need lights for the light position +// #include + +// in vec3 FragPos; // FragPos from GS (output per emitvertex) +// flat in int FragLayer; + +void main() +{ + // Only need to do anything with point lights, since sun and moon should already have nonlinear + // distance. + /*if (FragLayer > 0) */{ + // get distance between fragment and light source + // float lightDistance = length(FragPos - lights[FragLayer & 31].light_pos.xyz); + + // // // map to [0;1] range by dividing by far_plane + // lightDistance = lightDistance / screen_res.w;//FragPos.w;//screen_res.w; + + // // // write this as modified depth + // // // lightDistance = -1000.0 / (lightDistance + 10000.0); + // // // lightDistance /= screen_res.w; + // gl_FragDepth = lightDistance;// / /*FragPos.w;*/screen_res.w;//-1000.0 / (lightDistance + 1000.0);//lightDistance + } +} diff --git a/assets/voxygen/shaders/light-shadows-directed-vert.glsl b/assets/voxygen/shaders/light-shadows-directed-vert.glsl index 99aeaa61b1..38b5ff611d 100644 --- a/assets/voxygen/shaders/light-shadows-directed-vert.glsl +++ b/assets/voxygen/shaders/light-shadows-directed-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core // #extension ARB_texture_storage : enable #include @@ -22,13 +22,7 @@ // Currently, we only need globals for focus_off. #include // For shadow locals. -// #include - -layout (std140, set = 0, binding = 9) -uniform u_light_shadows { - mat4 shadowMatrices; - mat4 texture_mat; -}; +#include /* Accurate packed shadow maps for many lights at once! * @@ -36,13 +30,12 @@ uniform u_light_shadows { * * */ -layout(location = 0) in uint v_pos_norm; +in uint v_pos_norm; // in uint v_col_light; // in vec4 v_pos; -// layout(location = 1) in uint v_atlas_pos; // Light projection matrices. -layout (std140, set = 1, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; @@ -54,15 +47,17 @@ uniform u_locals { const int EXTRA_NEG_Z = 32768; void main() { +#if (SHADOW_MODE == SHADOW_MODE_MAP) vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - vec3 f_pos = f_chunk_pos + (model_offs - focus_off.xyz); + vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; // f_pos = v_pos; // vec3 f_pos = f_chunk_pos + model_offs; // gl_Position = v_pos + vec4(model_offs, 0.0); - gl_Position = /*all_mat * */shadowMatrices * vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); + gl_Position = /*all_mat * */shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); // gl_Position.z = -gl_Position.z; // gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; // vec4(v_pos, 0.0, 1.0); +#endif } diff --git a/assets/voxygen/shaders/light-shadows-figure-vert.glsl b/assets/voxygen/shaders/light-shadows-figure-vert.glsl index 1df58ae055..2178cae73e 100644 --- a/assets/voxygen/shaders/light-shadows-figure-vert.glsl +++ b/assets/voxygen/shaders/light-shadows-figure-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core // #extension ARB_texture_storage : enable #define FIGURE_SHADER @@ -24,13 +24,7 @@ // Currently, we only need globals for focus_off. #include // For shadow locals. -// #include - -layout (std140, set = 0, binding = 9) -uniform u_light_shadows { - mat4 shadowMatrices; - mat4 texture_mat; -}; +#include /* Accurate packed shadow maps for many lights at once! * @@ -38,12 +32,12 @@ uniform u_light_shadows { * * */ -layout(location = 0) in uint v_pos_norm; -layout(location = 1) in uint v_atlas_pos; +in uint v_pos_norm; +in uint v_atlas_pos; // in uint v_col_light; // in vec4 v_pos; -layout (std140, set = 1, binding = 0) +layout (std140) uniform u_locals { mat4 model_mat; vec4 highlight_col; @@ -61,7 +55,7 @@ struct BoneData { mat4 normals_mat; }; -layout (std140, set = 1, binding = 1) +layout (std140) uniform u_bones { // Warning: might not actually be 16 elements long. Don't index out of bounds! BoneData bones[16]; @@ -70,6 +64,7 @@ uniform u_bones { // out vec4 shadowMapCoord; void main() { +#if (SHADOW_MODE == SHADOW_MODE_MAP) uint bone_idx = (v_pos_norm >> 27) & 0xFu; vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; @@ -78,5 +73,6 @@ void main() { vec4(pos, 1.0) ).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/); - gl_Position = shadowMatrices * vec4(f_pos, 1.0); + gl_Position = shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos, 1.0); +#endif } diff --git a/assets/voxygen/shaders/light-shadows-frag.glsl b/assets/voxygen/shaders/light-shadows-frag.glsl index 3671ace978..ee3f84070b 100644 --- a/assets/voxygen/shaders/light-shadows-frag.glsl +++ b/assets/voxygen/shaders/light-shadows-frag.glsl @@ -2,7 +2,7 @@ // // However, in the future we might apply some depth transforms here. -#version 420 core +#version 330 core // #extension ARB_texture_storage : enable #include diff --git a/assets/voxygen/shaders/light-shadows-vert.glsl b/assets/voxygen/shaders/light-shadows-vert.glsl index 4bdbae25f4..66ca4bd041 100644 --- a/assets/voxygen/shaders/light-shadows-vert.glsl +++ b/assets/voxygen/shaders/light-shadows-vert.glsl @@ -26,12 +26,12 @@ * * */ -layout(location = 1) in uint v_pos_norm; +in uint v_pos_norm; // in uint v_col_light; // in vec4 v_pos; // Light projection matrices. -layout (std140, set = 1, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl index f55ae4e98c..6d8d6b2695 100644 --- a/assets/voxygen/shaders/lod-terrain-frag.glsl +++ b/assets/voxygen/shaders/lod-terrain-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -23,14 +23,14 @@ #include #include -layout(location = 0) in vec3 f_pos; -layout(location = 1) in vec3 f_norm; -layout(location = 2) in float pull_down; +in vec3 f_pos; +in vec3 f_norm; +in float pull_down; // in vec2 v_pos_orig; // in vec4 f_shadow; // in vec4 f_square; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; /// const vec4 sun_pos = vec4(0); // const vec4 light_pos[2] = vec4[](vec4(0), vec4(0)/*, vec3(00), vec3(0), vec3(0), vec3(0)*/); @@ -444,7 +444,7 @@ void main() { #endif #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); // float sun_shade_frac = 1.0; #elif (SHADOW_MODE == SHADOW_MODE_NONE) diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl index 48554ea74e..5f03cf934c 100644 --- a/assets/voxygen/shaders/lod-terrain-vert.glsl +++ b/assets/voxygen/shaders/lod-terrain-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -20,11 +20,16 @@ #include #include -layout(location = 0) in vec2 v_pos; +in vec2 v_pos; -layout(location = 0) out vec3 f_pos; -layout(location = 1) out vec3 f_norm; -layout(location = 2) out float pull_down; +layout (std140) +uniform u_locals { + vec4 nul; +}; + +out vec3 f_pos; +out vec3 f_norm; +out float pull_down; // out vec2 v_pos_orig; // out vec4 f_square; // out vec4 f_shadow; @@ -44,11 +49,8 @@ void main() { // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); - // TODO: disabled because it isn't designed to work with reverse depth - //float dist = distance(focus_pos.xy, f_pos.xy); - //pull_down = 0.2 / pow(dist / (view_distance.x * 0.9), 20.0); - - pull_down = 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0); + float dist = distance(focus_pos.xy, f_pos.xy); + pull_down = 0.2 / pow(dist / (view_distance.x * 0.9), 20.0); f_pos.z -= pull_down; // f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0)); @@ -98,9 +100,7 @@ void main() { all_mat * vec4(f_pos/*newRay*/, 1); // Pull up the depth to avoid drawing over voxels (biased according to VD) - // TODO: disabled because it isn't designed to work with reverse depth - //gl_Position.z += 0.1 * clamp((view_distance.x * 1.0 - dist) * 0.01, 0, 1); - + gl_Position.z += 0.1 * clamp((view_distance.x * 1.0 - dist) * 0.01, 0, 1); // gl_Position.z = -gl_Position.z / gl_Position.w; // gl_Position.z = -gl_Position.z / gl_Position.w; // gl_Position.z = -gl_Position.z * gl_Position.w; diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index bd04b3388d..b5efe862a0 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -16,12 +16,12 @@ #include -layout(location = 0) in vec3 f_pos; -layout(location = 1) flat in vec3 f_norm; -layout(location = 2) in vec4 f_col; -layout(location = 3) in float f_reflect; +in vec3 f_pos; +flat in vec3 f_norm; +in vec4 f_col; +in float f_reflect; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; #include #include @@ -40,7 +40,7 @@ void main() { #endif #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) float sun_shade_frac = 1.0; diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 4bad57d31e..06262c8300 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -17,22 +17,22 @@ #include #include -layout(location = 0) in vec3 v_pos; +in vec3 v_pos; // in uint v_col; -layout(location = 1) in uint v_norm_ao; -layout(location = 2) in float inst_time; -layout(location = 3) in float inst_lifespan; -layout(location = 4) in float inst_entropy; -layout(location = 5) in int inst_mode; -layout(location = 6) in vec3 inst_dir; -layout(location = 7) in vec3 inst_pos; +in uint v_norm_ao; +in vec3 inst_pos; +in float inst_time; +in float inst_lifespan; +in float inst_entropy; +in vec3 inst_dir; +in int inst_mode; -layout(location = 0) out vec3 f_pos; -layout(location = 1) flat out vec3 f_norm; -layout(location = 2) out vec4 f_col; -//layout(location = x) out float f_ao; -//layout(location = x) out float f_light; -layout(location = 3) out float f_reflect; +out vec3 f_pos; +flat out vec3 f_norm; +out vec4 f_col; +out float f_ao; +out float f_light; +out float f_reflect; const float SCALE = 1.0 / 11.0; diff --git a/assets/voxygen/shaders/point-light-shadows-vert.glsl b/assets/voxygen/shaders/point-light-shadows-vert.glsl deleted file mode 100644 index 7670a2f368..0000000000 --- a/assets/voxygen/shaders/point-light-shadows-vert.glsl +++ /dev/null @@ -1,61 +0,0 @@ -#version 420 core -// #extension ARB_texture_storage : enable - -#include - -#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION - -#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY - -#if (FLUID_MODE == FLUID_MODE_CHEAP) -#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE -#elif (FLUID_MODE == FLUID_MODE_SHINY) -#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE -#endif - -#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET - -#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN - -// Currently, we only need globals for focus_off. -#include - -/* Accurate packed shadow maps for many lights at once! - * - * Ideally, we would just write to a bitmask... - * - * */ - -layout(location = 0) in uint v_pos_norm; -// layout(location = 1) in uint v_atlas_pos; -// in uint v_col_light; -// in vec4 v_pos; - -// Light projection matrices. -layout (std140, set = 1, binding = 0) -uniform u_locals { - vec3 model_offs; - float load_time; - ivec4 atlas_offs; -}; - -// out vec4 shadowMapCoord; - -const int EXTRA_NEG_Z = 32768; - -layout( push_constant ) uniform PointLightMatrix { - mat4 lightShadowMatrix; -}; - -void main() { - vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; - // f_pos = v_pos; - // vec3 f_pos = f_chunk_pos + model_offs; - - // gl_Position = v_pos + vec4(model_offs, 0.0); - // gl_Position = /*all_mat * */vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); - // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; - // vec4(v_pos, 0.0, 1.0); - gl_Position = lightShadowMatrix * vec4(f_pos, 1.0); -} diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index aeba45f350..89ae9c1b59 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -22,21 +22,17 @@ #include #include -layout(set = 1, binding = 0) -uniform texture2D t_src_color; -layout(set = 1, binding = 1) -uniform sampler s_src_color; +//uniform sampler2D src_depth; +in vec2 f_pos; -layout(location = 0) in vec2 uv; - -layout (std140, set = 1, binding = 2) +layout (std140) uniform u_locals { mat4 proj_mat_inv; mat4 view_mat_inv; }; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); @@ -149,7 +145,33 @@ vec3 _illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitte // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); } +/* +float depth_at(vec2 uv) { + float buf_depth = texture(src_depth, uv).x; + vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0); + vec4 view_space = proj_mat_inv * clip_space; + view_space /= view_space.w; + return -view_space.z; +} + +vec3 wpos_at(vec2 uv) { + float buf_depth = texture(src_depth, uv).x * 2.0 - 1.0; + mat4 inv = view_mat_inv * proj_mat_inv;//inverse(all_mat); + vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0); + vec4 view_space = inv * clip_space; + view_space /= view_space.w; + if (buf_depth == 1.0) { + vec3 direction = normalize(view_space.xyz); + return direction.xyz * 100000.0 + cam_pos.xyz; + } else { + return view_space.xyz; + } +} +*/ + void main() { + vec2 uv = (f_pos + 1.0) * 0.5; + /* if (medium.x == 1u) { uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); } */ @@ -180,7 +202,7 @@ void main() { // float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0; - vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy); + vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy); // Tonemapping float exposure_offset = 1.0; diff --git a/assets/voxygen/shaders/postprocess-vert.glsl b/assets/voxygen/shaders/postprocess-vert.glsl index 21a6b3d8c2..35b786997f 100644 --- a/assets/voxygen/shaders/postprocess-vert.glsl +++ b/assets/voxygen/shaders/postprocess-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -18,16 +18,12 @@ #include -layout(location = 0) out vec2 uv; +in vec2 v_pos; + +out vec2 f_pos; void main() { - // Generate fullscreen triangle - vec2 v_pos = vec2( - float(gl_VertexIndex / 2) * 4.0 - 1.0, - float(gl_VertexIndex % 2) * 4.0 - 1.0 - ); + f_pos = v_pos; - uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5; - - gl_Position = vec4(v_pos, 0.0, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } diff --git a/assets/voxygen/shaders/skybox-frag.glsl b/assets/voxygen/shaders/skybox-frag.glsl index 37e1676520..aa3824cde6 100644 --- a/assets/voxygen/shaders/skybox-frag.glsl +++ b/assets/voxygen/shaders/skybox-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -20,9 +20,14 @@ #include #include -layout(location = 0) in vec3 f_pos; +in vec3 f_pos; -layout(location = 0) out vec4 tgt_color; +layout (std140) +uniform u_locals { + vec4 nul; +}; + +out vec4 tgt_color; void main() { // tgt_color = vec4(MU_SCATTER, 1.0); diff --git a/assets/voxygen/shaders/skybox-vert.glsl b/assets/voxygen/shaders/skybox-vert.glsl index 9f362fedb7..3bc29c8c6a 100644 --- a/assets/voxygen/shaders/skybox-vert.glsl +++ b/assets/voxygen/shaders/skybox-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -18,28 +18,31 @@ #include -layout(location = 0) in vec3 v_pos; +in vec3 v_pos; -layout(location = 0) out vec3 f_pos; +layout (std140) +uniform u_locals { + vec4 nul; +}; + +out vec3 f_pos; void main() { + /* vec3 v_pos = v_pos; + v_pos.y = -v_pos.y; */ f_pos = v_pos; // TODO: Make this position-independent to avoid rounding error jittering - // NOTE: we may or may not want to use an infinite projection here - // - // Essentially: using any finite projection is likely wrong here if we want - // to project out to infinity, but since we want to perturb the skybox as we - // move and we have stars now, the "right" answer is heavily dependent on - // how we compute cloud position and stuff. - // - // Infinite projections of cubemaps are nice because they can be oriented - // but still extend infinitely far. gl_Position = + /* proj_mat * + view_mat * */ all_mat * - vec4(v_pos + cam_pos.xyz, 1); + /* proj_mat * + view_mat * */ + vec4(/*100000 * */v_pos + cam_pos.xyz, 1); + // vec4(v_pos * (100000.0/* + 0.5*/) + cam_pos.xyz, 1); // gl_Position = vec4(gl_Position.xy, sign(gl_Position.z) * gl_Position.w, gl_Position.w); - gl_Position.z = 0; + gl_Position.z = gl_Position.w; // gl_Position.z = gl_Position.w - 0.000001;//0.0; // gl_Position.z = 1.0; // gl_Position.z = -1.0; diff --git a/assets/voxygen/shaders/sprite-frag.glsl b/assets/voxygen/shaders/sprite-frag.glsl index 6a9c97b0f2..3d6ff65d74 100644 --- a/assets/voxygen/shaders/sprite-frag.glsl +++ b/assets/voxygen/shaders/sprite-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -16,18 +16,31 @@ #include -layout(location = 0) in vec3 f_pos; -layout(location = 1) flat in vec3 f_norm; -layout(location = 2) flat in float f_select; -layout(location = 3) in vec2 f_uv_pos; -layout(location = 4) in vec2 f_inst_light; +in vec3 f_pos; +flat in vec3 f_norm; +flat in float f_select; +// flat in vec3 f_pos_norm; +in vec2 f_uv_pos; +in vec2 f_inst_light; +// flat in uint f_atlas_pos; +// in vec3 f_col; +// in float f_ao; +// in float f_light; +// in vec4 light_pos[2]; -layout(set = 3, binding = 0) -uniform texture2D t_col_light; -layout(set = 3, binding = 1) -uniform sampler s_col_light; +uniform sampler2D t_col_light; -layout(location = 0) out vec4 tgt_color; +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + +out vec4 tgt_color; #include #include @@ -36,31 +49,77 @@ layout(location = 0) out vec4 tgt_color; const float FADE_DIST = 32.0; void main() { - float f_ao, f_glow; - vec3 f_col = greedy_extract_col_light_glow(t_col_light, s_col_light, f_uv_pos, f_ao, f_glow); + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec2 f_uv_pos = vec2(768,1) + 0.5; + // vec2 f_uv_pos = vec2(760, 380);// + 0.5; + // vec2 f_uv_pos = vec2((uvec2(f_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)) + 0.5; + /* if (f_uv_pos.x < 757) { + discard; + } */ + // vec3 du = dFdx(f_pos); + // vec3 dv = dFdy(f_pos); + // vec3 f_norm = normalize(cross(du, dv)); + float f_ao, f_glow; + vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_ao, f_glow); + + // vec3 my_chunk_pos = f_pos_norm; + // tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0); + // tgt_color = vec4(f_uv_pos / texSize, 0.0, 1.0); + // tgt_color = vec4(f_col.rgb, 1.0); + // return; + // vec4 light_pos[2]; +//#if (SHADOW_MODE == SHADOW_MODE_MAP) +// // for (uint i = 0u; i < light_shadow_count.z; ++i) { +// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/; +// // } +// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/; +//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE) +// vec4 sun_pos = vec4(0.0); +//#endif vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); + // vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0); + // vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/); vec3 view_dir = -cam_to_frag; + /* vec3 sun_dir = get_sun_dir(time_of_day.x); + vec3 moon_dir = get_moon_dir(time_of_day.x); */ + // float sun_light = get_sun_brightness(sun_dir); + // float moon_light = get_moon_brightness(moon_dir); + #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) float f_alt = alt_at(f_pos.xy); + // float f_alt = f_pos.z; #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) float f_alt = f_pos.z; #endif #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); + // float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) - float sun_shade_frac = 1.0; + float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #endif - float moon_shade_frac = 1.0; + float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); + // float sun_shade_frac = horizon_at(f_pos, sun_dir); + // float moon_shade_frac = horizon_at(f_pos, moon_dir); + // Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence). + // Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing). + // float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5); + // NOTE: current assumption is that moon and sun shouldn't be out at the sae time. + // This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow + // for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally). + // float shade_frac = sun_shade_frac + moon_shade_frac; + // DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos); float point_shadow = shadow_at(f_pos, f_norm); - DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, f_pos); - DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac); + DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); + DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); - vec3 surf_color = f_col; + vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col); float alpha = 1.0; const float n2 = 1.5; const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); @@ -79,11 +138,42 @@ void main() { sun_info.block = f_inst_light.x; moon_info.block = f_inst_light.x; + // To account for prior saturation. + // float vert_light = pow(f_light, 1.5); + // vec3 light_frac = light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha); + /* light_frac += light_reflection_factor(f_norm, view_dir, vec3(1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(-1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, -1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); + light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, 1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); */ + + // vec3 light, diffuse_light, ambient_light; + // vec3 emitted_light, reflected_light; + // float point_shadow = shadow_at(f_pos,f_norm); + // vec3 point_light = light_at(f_pos, f_norm); + // vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); + // vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); float max_light = 0.0; - max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x, *//*cam_to_frag*/view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); + // reflected_light *= /*vert_light * */point_shadow * shade_frac; + // emitted_light *= /*vert_light * */point_shadow * max(shade_frac, MIN_SHADOW); + // max_light *= /*vert_light * */point_shadow * shade_frac; + // emitted_light *= point_shadow; + // reflected_light *= point_shadow; + // max_light *= point_shadow; + // get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); + // float point_shadow = shadow_at(f_pos, f_norm); + // diffuse_light *= f_light * point_shadow; + // ambient_light *= f_light * point_shadow; + // light += point_light; + // diffuse_light += point_light; + // reflected_light += point_light; max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); + /* vec3 point_light = light_at(f_pos, f_norm); + emitted_light += point_light; + reflected_light += point_light; */ + // float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15; vec3 glow = pow(f_inst_light.y, 3) * 4 * glow_light(f_pos); emitted_light += glow; @@ -92,9 +182,10 @@ void main() { reflected_light *= ao; surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); + // vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light); surf_color += f_select * (surf_color + 0.1) * vec3(0.15, 0.15, 0.15); + // tgt_color = vec4(color, 1.0); tgt_color = vec4(surf_color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1)); - //tgt_color = vec4(-f_norm, 1.0); } diff --git a/assets/voxygen/shaders/sprite-vert.glsl b/assets/voxygen/shaders/sprite-vert.glsl index e7cfd44fc8..e22235a1d8 100644 --- a/assets/voxygen/shaders/sprite-vert.glsl +++ b/assets/voxygen/shaders/sprite-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -16,114 +16,227 @@ #include #include -layout(location = 0) in vec4 inst_mat0; -layout(location = 1) in vec4 inst_mat1; -layout(location = 2) in vec4 inst_mat2; -layout(location = 3) in vec4 inst_mat3; -// TODO: is there a better way to pack the various vertex attributes? -// TODO: ori is unused -layout(location = 4) in uint inst_pos_ori; -layout(location = 5) in uint inst_vert_page; // NOTE: this could fit in less bits -// TODO: do we need this many bits for light and glow? -layout(location = 6) in float inst_light; -layout(location = 7) in float inst_glow; -layout(location = 8) in float model_wind_sway; // NOTE: this only varies per model -layout(location = 9) in float model_z_scale; // NOTE: this only varies per model +in vec3 v_pos; +in uint v_atlas_pos; +// in uint v_col; +in uint v_norm_ao; +in uint inst_pos_ori; +in vec4 inst_mat0; +in vec4 inst_mat1; +in vec4 inst_mat2; +in vec4 inst_mat3; +in vec4 inst_light; +in float inst_wind_sway; -layout(set = 0, binding = 12) restrict readonly buffer sprite_verts { - uvec2 verts[]; +struct SpriteLocals { + mat4 mat; + vec4 wind_sway; + vec4 offs; }; -layout (std140, set = 2, binding = 0) +layout (std140) +uniform u_locals { + mat4 mat; + vec4 wind_sway; + vec4 offs; + // SpriteLocals sprites[8]; +}; + +// struct Instance { +// mat4 inst_mat; +// vec3 inst_col; +// float inst_wind_sway; +// }; +// +// layout (std140) +// uniform u_ibuf { +// Instance sprite_instances[/*MAX_LAYER_FACES*/512]; +// }; + +//struct ShadowLocals { +// mat4 shadowMatrices; +// mat4 texture_mat; +//}; +// +//layout (std140) +//uniform u_light_shadows { +// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; +//}; + +layout (std140) uniform u_terrain_locals { vec3 model_offs; float load_time; ivec4 atlas_offs; }; -// TODO: consider grouping into vec4's -layout(location = 0) out vec3 f_pos; -layout(location = 1) flat out vec3 f_norm; -layout(location = 2) flat out float f_select; -layout(location = 3) out vec2 f_uv_pos; -layout(location = 4) out vec2 f_inst_light; +out vec3 f_pos; +flat out vec3 f_norm; +flat out float f_select; +// flat out vec3 f_pos_norm; +// out vec3 f_col; +// out float f_ao; +out vec2 f_uv_pos; +out vec2 f_inst_light; +// flat out uint f_atlas_pos; +// out vec3 light_pos[2]; +// out float f_light; const float SCALE = 1.0 / 11.0; const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2; const int EXTRA_NEG_Z = 32768; -const int VERT_EXTRA_NEG_Z = 128; -const uint VERT_PAGE_SIZE = 256; void main() { - // Matrix to transform this sprite instance from model space to chunk space + // vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); + // uint inst_ori = (inst_pos_ori >> 29) & 0x7u; + // SpriteLocals locals = sprites[inst_ori]; + // SpriteLocals locals = sprites; + // mat4 inst_mat = locals.mat; + // float inst_wind_sway = locals.wind_sway.w; + + // mat4 inst_mat = mat4(vec4(1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, 1, 0), vec4(5.5, 5.5, 0, 1)); + // float inst_wind_sway = 0.0; mat4 inst_mat; inst_mat[0] = inst_mat0; inst_mat[1] = inst_mat1; inst_mat[2] = inst_mat2; inst_mat[3] = inst_mat3; + /* Instance instances = sprite_instances[gl_InstanceID & 1023]; + mat4 inst_mat = instances.inst_mat; + vec3 inst_col = instances.inst_col; + float inst_wind_sway = instances.inst_wind_sway; */ + vec3 inst_offs = model_offs - focus_off.xyz; + // mat3 inst_mat; + // inst_mat[0] = inst_mat0.xyz; + // inst_mat[1] = inst_mat1.xyz; + // inst_mat[2] = inst_mat2.xyz; + // /* Instance instances = sprite_instances[gl_InstanceID & 1023]; + // mat4 inst_mat = instances.inst_mat; + // vec3 inst_col = instances.inst_col; + // float inst_wind_sway = instances.inst_wind_sway; */ + // float inst_wind_sway = wind_sway.w; + // vec3 inst_offs = model_offs - focus_off.xyz; - // Worldpos of the chunk that this sprite is in - vec3 chunk_offs = model_offs - focus_off.xyz; + f_inst_light = inst_light.xy; - f_inst_light = vec2(inst_light, inst_glow); + // vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs; - // Index of the vertex data in the 1D vertex texture - int vertex_index = int(uint(gl_VertexIndex) % VERT_PAGE_SIZE + inst_vert_page * VERT_PAGE_SIZE); - uvec2 pos_atlas_pos_norm_ao = verts[vertex_index]; - uint v_pos_norm = pos_atlas_pos_norm_ao.x; - uint v_atlas_pos = pos_atlas_pos_norm_ao.y; + // f_pos_norm = v_pos; - // Expand the model vertex position bits into float values - vec3 v_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 8, 16)) & uvec3(0xFFu, 0xFFu, 0x0FFFu)) - ivec3(0, 0, VERT_EXTRA_NEG_Z)); + // vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; + // vec3 sprite_pos = floor((inst_mat * vec4(0, 0, 0, 1)).xyz * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; + // vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(0, 0, 0, 1)).xyz - /* wind_sway.xyz * */offs.xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) - inst_offs; + // vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; - // Transform into chunk space and scale - f_pos = (inst_mat * vec4(v_pos, 1.0)).xyz; - // Transform info world space - f_pos += chunk_offs; + // vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0); + // f_pos = v_pos + (model_offs - focus_off.xyz); + + // vec3 v_pos = /*inst_mat*//*locals.*/wind_sway.xyz * v_pos; + vec3 v_pos_ = /*inst_mat*//*locals.*//*sprites[0].*/wind_sway.xyz * v_pos; + // vec3 v_pos = (/*inst_mat*/locals.mat * vec4(v_pos, 1)).xyz + vec3(0.5, 0.5, 0.0); + // f_pos = v_pos * SCALE + (inst_chunk_pos + model_offs - focus_off.xyz); + + // vec3 v_pos_ = (inst_mat * vec4(v_pos/* * SCALE*/, 1)).xyz; + // vec3 v_pos = (inst_mat * vec4(v_pos, 1)).xyz; + // f_pos = v_pos + (model_offs - focus_off.xyz); + + f_pos = (inst_mat * vec4(v_pos_, 1.0)).xyz * SCALE + inst_offs; // Terrain 'pop-in' effect f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); + // f_pos = (inst_mat * v_pos_) * SCALE + sprite_pos; - // Wind sway effect - f_pos += model_wind_sway * vec3( - sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), - sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), - 0.0 - // NOTE: could potentially replace `v_pos.z * model_z_scale` with a calculation using `inst_chunk_pos` from below - //) * pow(abs(v_pos.z * model_z_scale), 1.3) * SCALE_FACTOR; - ) * v_pos.z * model_z_scale * SCALE_FACTOR; + // f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz + (model_offs - focus_off.xyz); + // f_pos = v_pos_ + (inst_chunk_pos + model_offs - focus_off.xyz + vec3(0.5, 0.5, 0.0)); + // f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0)); - // Determine normal - // TODO: do changes here effect perf on vulkan - // TODO: dx12 doesn't like dynamic index - // TODO: use mix? - // Shader@0x000001AABD89BEE0(112,43-53): error X4576: Input array signature parameter cannot be indexed dynamically. - //vec3 norm = (inst_mat[(v_pos_norm >> 30u) & 3u].xyz); - uint index = v_pos_norm >> 30u & 3u; - vec3 norm; - if (index == 0) { - norm = (inst_mat[0].xyz); - } else if (index == 1) { - norm = (inst_mat[1].xyz); - } else { - norm = (inst_mat[2].xyz); + // Wind waving + /* const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1); + const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1); + const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR; + const float xy_bias = sin(tick.x * 0.25); + const float z_bias = xy_bias * t_scale; + mat3 shear = mat4( + vec3(x_scale , 0.0, 0.0, 0.0), + vec3(0.0, y_scale, 0.0, 0.0), + vec3(0.0, 0.0, z_bias, 0.0), + vec3(0.0, 0.0, (1.0 / z_bias), 0.0) + ); */ + // const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1); + // const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1); + // const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR; + // const float xy_bias = sin(tick.x * 0.25); + // const float z_bias = xy_bias * t_scale; + // vec3 rotate = inst_wind_sway * vec3( + // sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), + // sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), + // 0.0 + // ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR; + // + // mat3 shear = mat4( + // vec3(x_scale * , 0.0, 0.0, 0.0), + // vec3(0.0, y_scale, 0.0, 0.0), + // vec3(0.0, 0.0, z_bias, 0.0), + // vec3(0.0, 0.0, (1.0 / z_bias), 0.0) + // ); + /*if (wind_sway.w >= 0.4) */{ + f_pos += /*inst_wind_sway*/wind_sway.w * vec3( + sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), + sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), + 0.0 + ) * 4 * v_pos_.z * /*0.2;*/SCALE_FACTOR; } - f_norm = normalize(mix(-norm, norm, v_pos_norm >> 29u & 1u)); + // First 3 normals are negative, next 3 are positive + // vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); + // uint norm_idx = (v_norm_ao >> 0) & 0x7u; + // f_norm = (inst_mat * vec4(normals[], 0)).xyz; - // Expand atlas tex coords to floats - // NOTE: Could defer to fragment shader if we are vert heavy - f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));; + // TODO: Consider adding a second, already-normalized (i.e. unscaled) matrix. + // vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz); + // vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u]); - // Position of the sprite block in the chunk - // Used solely for highlighting the selected sprite - vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); - // Select glowing - vec3 sprite_pos = inst_chunk_pos + chunk_offs; - f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos) ? 1.0 : 0.0; + // vec3 norm = bone_data.normals_mat[axis_idx].xyz; + // norm = normalize(norm); + // norm = norm / SCALE_FACTOR / locals.wind_sway.xyz; + // norm = norm / (norm.x + norm.y + norm.z); + // vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); + + // // Calculate normal here rather than for each pixel in the fragment shader + // f_norm = normalize(( + // combined_mat * + // vec4(norm, 0) + // ).xyz); + + vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz); + f_norm = mix(-norm, norm, v_norm_ao & 1u); + + /* vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0; + f_col = srgb_to_linear(col) * srgb_to_linear(inst_col); + f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; */ + f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));/* + 0.5*/; + // f_atlas_pos = v_atlas_pos; + /* for (uint i = 0u; i < light_shadow_count.z; ++i) { + light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0)); + } */ + + // // Select glowing + // if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { + // f_col *= 4.0; + // } + // f_light = 1.0; + // if (select_pos.w > 0) */{ + vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; + f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 1.0 : 0.0; + // } gl_Position = all_mat * vec4(f_pos, 1); + // gl_Position.z = -gl_Position.z; + // gl_Position.z = -gl_Position.z / gl_Position.w; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -gl_Position.z / 100.0; + // gl_Position.z = -1000.0 / (gl_Position.z + 10000.0); } diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl index d59bfa7964..12b4fb15af 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core // #extension GL_ARB_texture_storage : require #include @@ -22,12 +22,12 @@ #include #include -layout(location = 0) in vec3 f_pos; +in vec3 f_pos; // in float f_ao; // in vec3 f_chunk_pos; // #ifdef FLUID_MODE_SHINY -layout(location = 1) flat in uint f_pos_norm; -layout(location = 2) flat in float f_load_time; +flat in uint f_pos_norm; +flat in float f_load_time; // #else // const uint f_pos_norm = 0u; // #endif @@ -35,7 +35,7 @@ layout(location = 2) flat in float f_load_time; // in vec4 f_shadow; // in vec3 f_col; // in float f_light; -/*centroid */layout(location = 3) in vec2 f_uv_pos; +/*centroid */in vec2 f_uv_pos; // in vec3 light_pos[2]; // const vec3 light_pos[6] = vec3[](vec3(0), vec3(0), vec3(00), vec3(0), vec3(0), vec3(0)); @@ -45,19 +45,16 @@ in vec4 sun_pos; const vec4 sun_pos = vec4(0.0); #endif */ -layout(set = 3, binding = 0) -uniform texture2D t_col_light; -layout(set = 3, binding = 1) -uniform sampler s_col_light; +uniform sampler2D t_col_light; -layout (std140, set = 2, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; ivec4 atlas_offs; }; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; #include #include @@ -85,7 +82,7 @@ void main() { // vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize); // float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; float f_light, f_glow; - vec3 f_col = greedy_extract_col_light_glow(t_col_light, s_col_light, f_uv_pos, f_light, f_glow); + vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_light, f_glow); //float f_light = (uint(texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).r * 255.0) & 0x1Fu) / 31.0; // vec2 texSize = textureSize(t_col_light, 0); // float f_light = texture(t_col_light, f_uv_pos/* + vec2(atlas_offs.xy)*/).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; @@ -219,7 +216,7 @@ void main() { // float f_alt = alt_at(f_pos.xy); // vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) - vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy)); + vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); #elif (SHADOW_MODE == SHADOW_MODE_NONE) float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl index 7fac4a0d2e..821b380d94 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -1,4 +1,4 @@ -#version 420 core +#version 330 core #include @@ -24,15 +24,14 @@ #include -layout(location = 0) in uint v_pos_norm; +in uint v_pos_norm; // in uint v_col_light; -layout(location = 1) in uint v_atlas_pos; +in uint v_atlas_pos; -layout (std140, set = 2, binding = 0) +layout (std140) uniform u_locals { vec3 model_offs; float load_time; - // TODO: consider whether these need to be signed ivec4 atlas_offs; }; @@ -46,10 +45,10 @@ uniform u_locals { // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; //}; -layout(location = 0) out vec3 f_pos; +out vec3 f_pos; // #ifdef FLUID_MODE_SHINY -layout(location = 1) flat out uint f_pos_norm; -layout(location = 2) flat out float f_load_time; +flat out uint f_pos_norm; +flat out float f_load_time; // #if (SHADOW_MODE == SHADOW_MODE_MAP) // out vec4 sun_pos; @@ -61,7 +60,7 @@ layout(location = 2) flat out float f_load_time; // out vec3 f_col; // out vec3 f_chunk_pos; // out float f_ao; -/*centroid */layout(location = 3) out vec2 f_uv_pos; +/*centroid */out vec2 f_uv_pos; // out vec3 light_pos[2]; // out float f_light; @@ -156,7 +155,7 @@ void main() { #ifdef HAS_SHADOW_MAPS gl_Position = - /*all_mat*/shadowMatrices/*texture_mat*/ * + /*all_mat*/shadowMats[0].shadowMatrices/*texture_mat*/ * vec4(f_pos/*newRay*/, 1); gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); #else diff --git a/assets/voxygen/shaders/ui-frag.glsl b/assets/voxygen/shaders/ui-frag.glsl index d9010a2c90..693926e07b 100644 --- a/assets/voxygen/shaders/ui-frag.glsl +++ b/assets/voxygen/shaders/ui-frag.glsl @@ -1,31 +1,28 @@ -#version 420 core +#version 330 core #include -layout(location = 0) in vec2 f_uv; -layout(location = 1) in vec4 f_color; -layout(location = 2) flat in uint f_mode; +in vec2 f_uv; +in vec4 f_color; +flat in uint f_mode; -layout (std140, set = 1, binding = 0) +layout (std140) uniform u_locals { vec4 w_pos; }; -layout(set = 2, binding = 0) -uniform texture2D t_tex; -layout(set = 2, binding = 1) -uniform sampler s_tex; +uniform sampler2D u_tex; -layout(location = 0) out vec4 tgt_color; +out vec4 tgt_color; void main() { // Text if (f_mode == uint(0)) { - tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(sampler2D(t_tex, s_tex), f_uv).a); + tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(u_tex, f_uv).a); // Image // HACK: bit 0 is set for both ordinary and north-facing images. } else if ((f_mode & uint(1)) == uint(1)) { - tgt_color = f_color * texture(sampler2D(t_tex, s_tex), f_uv); + tgt_color = f_color * texture(u_tex, f_uv); // 2D Geometry } else if (f_mode == uint(2)) { tgt_color = f_color; diff --git a/assets/voxygen/shaders/ui-vert.glsl b/assets/voxygen/shaders/ui-vert.glsl index 7c25f01fd3..9f52da73f4 100644 --- a/assets/voxygen/shaders/ui-vert.glsl +++ b/assets/voxygen/shaders/ui-vert.glsl @@ -1,26 +1,23 @@ -#version 420 core +#version 330 core #include -layout(location = 0) in vec2 v_pos; -layout(location = 1) in vec2 v_uv; -layout(location = 2) in vec4 v_color; -layout(location = 3) in vec2 v_center; -layout(location = 4) in uint v_mode; +in vec2 v_pos; +in vec2 v_uv; +in vec2 v_center; +in vec4 v_color; +in uint v_mode; -layout (std140, set = 1, binding = 0) +layout (std140) uniform u_locals { vec4 w_pos; }; -layout(set = 2, binding = 0) -uniform texture2D t_tex; -layout(set = 2, binding = 1) -uniform sampler s_tex; +uniform sampler2D u_tex; -layout(location = 0) out vec2 f_uv; -layout(location = 1) out vec4 f_color; -layout(location = 2) flat out uint f_mode; +out vec2 f_uv; +flat out uint f_mode; +out vec4 f_color; void main() { f_color = v_color; @@ -33,13 +30,13 @@ void main() { f_uv = v_uv; // Fixed scale In-game element vec4 projected_pos = /*proj_mat * view_mat*/all_mat * vec4(w_pos.xyz - focus_off.xyz, 1.0); - gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, 0.5, /*projected_pos.w*/1.0); + gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, -1.0, /*projected_pos.w*/1.0); } else if (v_mode == uint(3)) { // HACK: North facing source rectangle. - gl_Position = vec4(v_pos, 0.5, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). - vec2 aspect_ratio = textureSize(sampler2D(t_tex, s_tex), 0).yx; + vec2 aspect_ratio = textureSize(u_tex, 0).yx; mat2 look_at = mat2(look_at_dir.y, look_at_dir.x, -look_at_dir.x, look_at_dir.y); vec2 v_centered = (v_uv - v_center) / aspect_ratio; vec2 v_rotated = look_at * v_centered; @@ -53,12 +50,11 @@ void main() { mat2 look_at = mat2(look_at_dir.y, -look_at_dir.x, look_at_dir.x, look_at_dir.y); vec2 v_centered = (v_pos - v_center) / aspect_ratio; vec2 v_rotated = look_at * v_centered; - gl_Position = vec4(aspect_ratio * v_rotated + v_center, 0.5, 1.0); + gl_Position = vec4(aspect_ratio * v_rotated + v_center, -1.0, 1.0); } else { // Interface element f_uv = v_uv; - gl_Position = vec4(v_pos, 0.5, 1.0); + gl_Position = vec4(v_pos, -1.0, 1.0); } - f_mode = v_mode; } diff --git a/assets/voxygen/texture/waves.png b/assets/voxygen/texture/waves.png new file mode 100644 index 0000000000..c635e805af --- /dev/null +++ b/assets/voxygen/texture/waves.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:929041d2f1e54a5f2960622bcc90abd6c8119e59b381ce0218e198803256ce02 +size 7797 diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index 3325612858..4f9d8ed951 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -58,10 +58,7 @@ macro_rules! span { }; } -#[cfg(feature = "tracy")] -pub struct ProfSpan(pub tracy_client::Span); -#[cfg(not(feature = "tracy"))] -pub struct ProfSpan; +pub struct DummySpan; /// Like the span macro but only used when profiling and not in regular tracing /// operations @@ -69,16 +66,16 @@ pub struct ProfSpan; macro_rules! prof_span { ($guard_name:tt, $name:expr) => { #[cfg(feature = "tracy")] - let $guard_name = $crate::ProfSpan($crate::tracy_client::Span::new( + let $guard_name = $crate::tracy_client::Span::new( $name, "", module_path!(), line!(), // No callstack since this has significant overhead 0, - )); + ); #[cfg(not(feature = "tracy"))] - let $guard_name = $crate::ProfSpan; + let $guard_name = $crate::DummySpan; }; } diff --git a/common/frontend/src/lib.rs b/common/frontend/src/lib.rs index cb61941670..40278de603 100644 --- a/common/frontend/src/lib.rs +++ b/common/frontend/src/lib.rs @@ -47,6 +47,7 @@ where // this crate would be veloren_voxygen=debug. let base_exceptions = |env: EnvFilter| { env.add_directive("dot_vox::parser=warn".parse().unwrap()) + .add_directive("gfx_device_gl=warn".parse().unwrap()) .add_directive("veloren_common::trade=info".parse().unwrap()) .add_directive("veloren_world::sim=info".parse().unwrap()) .add_directive("veloren_world::civ=info".parse().unwrap()) @@ -57,8 +58,6 @@ where .add_directive("h2=info".parse().unwrap()) .add_directive("tokio_util=info".parse().unwrap()) .add_directive("rustls=info".parse().unwrap()) - .add_directive("wgpu_core=info".parse().unwrap()) - .add_directive("wgpu_core::device=warn".parse().unwrap()) .add_directive("veloren_network_protocol=info".parse().unwrap()) .add_directive("quinn_proto::connection=info".parse().unwrap()) .add_directive( diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 90c691354e..02fd3f6291 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -22,13 +22,14 @@ runtimeLibs = ["libGL", "xorg.libX11", "xorg.libXcursor", "xorg.libXrandr", "xor buildInputs = ["xorg.libxcb"] [features] +gl = ["gfx_device_gl", "gfx_gl"] hot-anim = ["anim/use-dyn-lib"] singleplayer = ["server"] simd = ["vek/platform_intrinsics"] -tracy = ["profiling", "profiling/profile-with-tracy", "common/tracy", "common-ecs/tracy", "common-frontend/tracy", "common-net/tracy", "common-systems/tracy", "common-state/tracy", "client/tracy"] +tracy = ["common/tracy", "common-ecs/tracy", "common-frontend/tracy", "common-net/tracy", "common-systems/tracy", "common-state/tracy", "client/tracy"] plugins = ["client/plugins"] -default = ["singleplayer", "native-dialog", "plugins", "simd"] +default = ["gl", "singleplayer", "native-dialog", "plugins", "simd"] [dependencies] client = {package = "veloren-client", path = "../client"} @@ -44,11 +45,12 @@ anim = {package = "veloren-voxygen-anim", path = "anim"} i18n = {package = "veloren-i18n", path = "i18n"} # Graphics +gfx = "0.18.2" +gfx_device_gl = {version = "0.16.2", optional = true} +gfx_gl = {version = "0.6.1", optional = true} +glutin = "0.26.0" +old_school_gfx_glutin_ext = "0.26" winit = {version = "0.24.0", features = ["serde"]} -wgpu = { version = "=0.8.0", features = ["trace", "cross"] } -wgpu-profiler = { git = "https://github.com/Imberflur/wgpu-profiler", tag = "wgpu-0.8" } -bytemuck = { version="1.4", features=["derive"] } -shaderc = "0.6.2" # Ui conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} @@ -85,7 +87,9 @@ crossbeam-channel = "0.5" directories-next = "2.0" dot_vox = "4.0" enum-iterator = "0.6" -futures-executor = "0.3" +strum = "0.20" +strum_macros = "0.20" +glsl-include = "0.3.1" guillotiere = "0.6" hashbrown = {version = "0.9", features = ["rayon", "serde", "nightly"]} image = {version = "0.23.12", default-features = false, features = ["ico", "png"]} @@ -94,12 +98,9 @@ native-dialog = { version = "0.5.2", optional = true } num = "0.4" ordered-float = { version = "2.0.1", default-features = false } rand = "0.8" -rayon = "1.5" rodio = {version = "0.13", default-features = false, features = ["vorbis"]} ron = {version = "0.6", default-features = false} serde = {version = "1.0", features = [ "rc", "derive" ]} -strum = "0.20" -strum_macros = "0.20" treeculler = "0.2" tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } num_cpus = "1.0" @@ -109,7 +110,6 @@ itertools = "0.10.0" # Tracy tracing = "0.1" -profiling = { version = "1.0.1", default-features = false, optional = true } [target.'cfg(target_os = "macos")'.dependencies] dispatch = "0.1.4" diff --git a/voxygen/anim/Cargo.toml b/voxygen/anim/Cargo.toml index 68f9ee5290..d56153f062 100644 --- a/voxygen/anim/Cargo.toml +++ b/voxygen/anim/Cargo.toml @@ -20,4 +20,3 @@ libloading = {version = "0.7", optional = true} notify = {version = "5.0.0-pre.2", optional = true} tracing = {version = "0.1", optional = true} vek = {version = "=0.14.1", features = ["serde"]} -bytemuck = { version="1.4", features=["derive"] } diff --git a/voxygen/anim/src/lib.rs b/voxygen/anim/src/lib.rs index b11e1410ad..d401074bda 100644 --- a/voxygen/anim/src/lib.rs +++ b/voxygen/anim/src/lib.rs @@ -74,19 +74,16 @@ pub use dyn_lib::init; use std::ffi::CStr; use self::vek::*; -use bytemuck::{Pod, Zeroable}; type MatRaw = [[f32; 4]; 4]; -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)] -pub struct FigureBoneData(pub MatRaw, pub MatRaw); +pub type FigureBoneData = (MatRaw, MatRaw); pub const MAX_BONE_COUNT: usize = 16; fn make_bone(mat: Mat4) -> FigureBoneData { let normal = mat.map_cols(Vec4::normalized); - FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays()) + (mat.into_col_arrays(), normal.into_col_arrays()) } pub type Bone = Transform; diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs index a1e7e3a9c5..0d1f0d135a 100644 --- a/voxygen/benches/meshing_benchmark.rs +++ b/voxygen/benches/meshing_benchmark.rs @@ -5,7 +5,7 @@ use common::{ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::sync::Arc; use vek::*; -use veloren_voxygen::{mesh::terrain::generate_mesh, scene::terrain::BlocksOfInterest}; +use veloren_voxygen::{mesh::Meshable, scene::terrain::BlocksOfInterest}; use world::{sim, World}; const CENTER: Vec2 = Vec2 { x: 512, y: 512 }; @@ -142,10 +142,11 @@ pub fn criterion_benchmark(c: &mut Criterion) { let (volume, range) = sample(Vec2::new(x, y)); meshing_benches.bench_function(&format!("Terrain mesh {}, {}", x, y), move |b| { b.iter(|| { - generate_mesh( - black_box(&volume), - black_box((range, Vec2::new(8192, 8192), &BlocksOfInterest::default())), - ) + volume.generate_mesh(black_box(( + range, + Vec2::new(8192, 8192), + &BlocksOfInterest::default(), + ))) }) }); } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 7791d17629..205e79c450 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -55,7 +55,7 @@ use crate::{ ecs::{comp as vcomp, comp::HpFloaterList}, hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent}, i18n::Localization, - render::UiDrawer, + render::{Consts, Globals, Renderer}, scene::camera::{self, Camera}, session::{ settings_change::{Chat as ChatChange, Interface as InterfaceChange, SettingsChange}, @@ -238,7 +238,6 @@ widget_ids! { num_lights, num_figures, num_particles, - gpu_timings[], // Game Version version, @@ -2195,33 +2194,6 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_particles, ui_widgets); - // GPU timing for different pipelines - let gpu_timings = global_state.window.renderer().timings(); - if !gpu_timings.is_empty() { - let num_timings = gpu_timings.len(); - // Make sure we have enough ids - if self.ids.gpu_timings.len() < num_timings { - self.ids - .gpu_timings - .resize(num_timings, &mut ui_widgets.widget_id_generator()); - } - for (i, timing) in gpu_timings.iter().enumerate() { - Text::new(&format!( - "{:16}{:.3} ms", - &format!("{}:", timing.1), - timing.2 * 1000.0, - )) - .color(TEXT_COLOR) - .down(5.0) - .x_place_on( - ui_widgets.window, - conrod_core::position::Place::Start(Some(5.0 + 10.0 * timing.0 as f64)), - ) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .set(self.ids.gpu_timings[i], ui_widgets); - } - } // Help Window if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) { Text::new( @@ -2230,7 +2202,7 @@ impl Hud { .replace("{key}", help_key.display_string(key_layout).as_str()), ) .color(TEXT_COLOR) - .down(5.0) + .down_from(self.ids.num_particles, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.help_info, ui_widgets); @@ -3647,11 +3619,11 @@ impl Hud { events } - pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { + pub fn render(&self, renderer: &mut Renderer, globals: &Consts) { span!(_guard, "render", "Hud::render"); // Don't show anything if the UI is toggled off. if self.show.ui { - self.ui.render(drawer); + self.ui.render(renderer, Some(globals)); } } diff --git a/voxygen/src/hud/settings_window/interface.rs b/voxygen/src/hud/settings_window/interface.rs index 4a4c5cb32f..0d67613e96 100644 --- a/voxygen/src/hud/settings_window/interface.rs +++ b/voxygen/src/hud/settings_window/interface.rs @@ -36,8 +36,6 @@ widget_ids! { load_tips_button_label, debug_button, debug_button_label, - hitboxes_button, - hitboxes_button_label, ch_title, ch_transp_slider, ch_transp_value, @@ -241,33 +239,9 @@ impl<'a> Widget for Interface<'a> { .color(TEXT_COLOR) .set(state.ids.debug_button_label, ui); - // Hitboxes - let show_hitboxes = ToggleButton::new( - self.global_state.settings.interface.toggle_hitboxes, - self.imgs.checkbox, - self.imgs.checkbox_checked, - ) - .w_h(18.0, 18.0) - .down_from(state.ids.debug_button, 8.0) - .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) - .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) - .set(state.ids.hitboxes_button, ui); - - if self.global_state.settings.interface.toggle_hitboxes != show_hitboxes { - events.push(ToggleHitboxes(show_hitboxes)); - } - - Text::new(&self.localized_strings.get("hud.settings.show_hitboxes")) - .right_from(state.ids.hitboxes_button, 10.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.hitboxes_button) - .color(TEXT_COLOR) - .set(state.ids.hitboxes_button_label, ui); - // Ui Scale Text::new(&self.localized_strings.get("hud.settings.ui_scale")) - .down_from(state.ids.hitboxes_button, 20.0) + .down_from(state.ids.debug_button, 20.0) .font_size(self.fonts.cyri.scale(18)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c064da77de..835bb1eb69 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -7,10 +7,10 @@ use crate::{ }, i18n::Localization, render::{ - AaMode, CloudMode, FluidMode, LightingMode, PresentMode, RenderMode, ShadowMapMode, - ShadowMode, UpscaleMode, + AaMode, CloudMode, FluidMode, LightingMode, RenderMode, ShadowMapMode, ShadowMode, + UpscaleMode, }, - session::settings_change::Graphics as GraphicsChange, + session::settings_change::{Graphics as GraphicsChange, Graphics::*}, settings::Fps, ui::{fonts::Fonts, ImageSlider, ToggleButton}, window::{FullScreenSettings, FullscreenMode}, @@ -35,7 +35,6 @@ widget_ids! { window_scrollbar, reset_graphics_button, fps_counter, - pipeline_recreation_text, vd_slider, vd_text, vd_value, @@ -51,8 +50,6 @@ widget_ids! { max_fps_slider, max_fps_text, max_fps_value, - present_mode_text, - present_mode_list, fov_slider, fov_text, fov_value, @@ -83,9 +80,6 @@ widget_ids! { refresh_rate, refresh_rate_label, // - gpu_profiler_button, - gpu_profiler_label, - // particles_button, particles_label, lossy_terrain_compression_button, @@ -211,24 +205,6 @@ impl<'a> Widget for Video<'a> { .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(18)) .set(state.ids.fps_counter, ui); - - // Pipeline recreation status - if let Some((total, complete)) = self - .global_state - .window - .renderer() - .pipeline_recreation_status() - { - Text::new(&format!("Rebuilding pipelines: ({}/{})", complete, total)) - .down_from(state.ids.fps_counter, 10.0) - .align_right_of(state.ids.fps_counter) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - // TODO: make color pulse or something - .color(TEXT_COLOR) - .set(state.ids.pipeline_recreation_text, ui); - } - // View Distance Text::new(&self.localized_strings.get("hud.settings.view_distance")) .top_left_with_margins_on(state.ids.window, 10.0, 10.0) @@ -253,7 +229,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.vd_slider, ui) { - events.push(GraphicsChange::AdjustViewDistance(new_val)); + events.push(AdjustViewDistance(new_val)); } Text::new(&format!( @@ -291,7 +267,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.max_fps_slider, ui) { - events.push(GraphicsChange::ChangeMaxFPS(FPS_CHOICES[which])); + events.push(ChangeMaxFPS(FPS_CHOICES[which])); } Text::new(&self.global_state.settings.graphics.max_fps.to_string()) @@ -301,53 +277,6 @@ impl<'a> Widget for Video<'a> { .color(TEXT_COLOR) .set(state.ids.max_fps_value, ui); - // Get render mode - let render_mode = &self.global_state.settings.graphics.render_mode; - - // Present Mode - Text::new(&self.localized_strings.get("hud.settings.present_mode")) - .down_from(state.ids.vd_slider, 10.0) - .right_from(state.ids.max_fps_value, 30.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.present_mode_text, ui); - - let mode_list = [ - PresentMode::Fifo, - PresentMode::Mailbox, - PresentMode::Immediate, - ]; - let mode_label_list = [ - &self.localized_strings.get("hud.settings.present_mode.fifo"), - &self - .localized_strings - .get("hud.settings.present_mode.mailbox"), - &self - .localized_strings - .get("hud.settings.present_mode.immediate"), - ]; - - // Get which present mode is currently active - let selected = mode_list - .iter() - .position(|x| *x == render_mode.present_mode); - - if let Some(clicked) = DropDownList::new(&mode_label_list, selected) - .w_h(120.0, 22.0) - .color(MENU_BG) - .label_color(TEXT_COLOR) - .label_font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.present_mode_text, 8.0) - .align_middle_x() - .set(state.ids.present_mode_list, ui) - { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { - present_mode: mode_list[clicked], - ..render_mode.clone() - }))); - } - // FOV Text::new(&self.localized_strings.get("hud.settings.fov")) .down_from(state.ids.max_fps_slider, 10.0) @@ -370,7 +299,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.fov_slider, ui) { - events.push(GraphicsChange::ChangeFOV(new_val)); + events.push(ChangeFOV(new_val)); } Text::new(&format!("{}", self.global_state.settings.graphics.fov)) @@ -403,7 +332,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.lod_detail_slider, ui) { - events.push(GraphicsChange::AdjustLodDetail( + events.push(AdjustLodDetail( (5.0f32.powf(new_val as f32 / 10.0) * 100.0) as u32, )); } @@ -440,9 +369,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.gamma_slider, ui) { - events.push(GraphicsChange::ChangeGamma( - 2.0f32.powf(new_val as f32 / 8.0), - )); + events.push(ChangeGamma(2.0f32.powf(new_val as f32 / 8.0))); } Text::new(&format!("{:.2}", self.global_state.settings.graphics.gamma)) @@ -467,7 +394,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.exposure_slider, ui) { - events.push(GraphicsChange::ChangeExposure(new_val as f32 / 16.0)); + events.push(ChangeExposure(new_val as f32 / 16.0)); } Text::new(&self.localized_strings.get("hud.settings.exposure")) @@ -505,7 +432,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.ambiance_slider, ui) { - events.push(GraphicsChange::ChangeAmbiance(new_val as f32)); + events.push(ChangeAmbiance(new_val as f32)); } Text::new(&self.localized_strings.get("hud.settings.ambiance")) .up_from(state.ids.ambiance_slider, 8.0) @@ -541,7 +468,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.sprite_dist_slider, ui) { - events.push(GraphicsChange::AdjustSpriteRenderDistance(new_val)); + events.push(AdjustSpriteRenderDistance(new_val)); } Text::new( &self @@ -581,7 +508,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.figure_dist_slider, ui) { - events.push(GraphicsChange::AdjustFigureLoDRenderDistance(new_val)); + events.push(AdjustFigureLoDRenderDistance(new_val)); } Text::new( &self @@ -607,6 +534,8 @@ impl<'a> Widget for Video<'a> { .color(TEXT_COLOR) .set(state.ids.figure_dist_value, ui); + let render_mode = &self.global_state.settings.graphics.render_mode; + // AaMode Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) .down_from(state.ids.gamma_slider, 8.0) @@ -643,7 +572,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.aa_mode_text, 8.0) .set(state.ids.aa_mode_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { aa: mode_list[clicked], ..render_mode.clone() }))); @@ -683,7 +612,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.upscale_factor_text, 8.0) .set(state.ids.upscale_factor_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { upscale_mode: UpscaleMode { factor: upscale_factors[clicked], }, @@ -741,7 +670,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.cloud_mode_text, 8.0) .set(state.ids.cloud_mode_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { cloud: mode_list[clicked], ..render_mode.clone() }))); @@ -780,7 +709,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.fluid_mode_text, 8.0) .set(state.ids.fluid_mode_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { fluid: mode_list[clicked], ..render_mode.clone() }))); @@ -826,7 +755,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.lighting_mode_text, 8.0) .set(state.ids.lighting_mode_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { lighting: mode_list[clicked], ..render_mode.clone() }))); @@ -873,7 +802,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.shadow_mode_text, 8.0) .set(state.ids.shadow_mode_list, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { shadow: mode_list[clicked], ..render_mode.clone() }))); @@ -906,7 +835,7 @@ impl<'a> Widget for Video<'a> { .pad_track((5.0, 5.0)) .set(state.ids.shadow_mode_map_resolution_slider, ui) { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { + events.push(ChangeRenderMode(Box::new(RenderMode { shadow: ShadowMode::Map(ShadowMapMode { resolution: 2.0f32.powf(f32::from(new_val) / 4.0), }), @@ -924,37 +853,11 @@ impl<'a> Widget for Video<'a> { .set(state.ids.shadow_mode_map_resolution_value, ui); } - // GPU Profiler - Text::new(&self.localized_strings.get("hud.settings.gpu_profiler")) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.shadow_mode_list, 8.0) - .color(TEXT_COLOR) - .set(state.ids.gpu_profiler_label, ui); - - let gpu_profiler_enabled = ToggleButton::new( - render_mode.profiler_enabled, - self.imgs.checkbox, - self.imgs.checkbox_checked, - ) - .w_h(18.0, 18.0) - .right_from(state.ids.gpu_profiler_label, 10.0) - .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) - .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) - .set(state.ids.gpu_profiler_button, ui); - - if render_mode.profiler_enabled != gpu_profiler_enabled { - events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode { - profiler_enabled: gpu_profiler_enabled, - ..render_mode.clone() - }))); - } - // Particles Text::new(&self.localized_strings.get("hud.settings.particles")) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .down_from(state.ids.gpu_profiler_label, 8.0) + .down_from(state.ids.shadow_mode_list, 8.0) .color(TEXT_COLOR) .set(state.ids.particles_label, ui); @@ -970,7 +873,7 @@ impl<'a> Widget for Video<'a> { .set(state.ids.particles_button, ui); if self.global_state.settings.graphics.particles_enabled != particles_enabled { - events.push(GraphicsChange::ToggleParticlesEnabled(particles_enabled)); + events.push(ToggleParticlesEnabled(particles_enabled)); } // Lossy terrain compression @@ -1006,9 +909,7 @@ impl<'a> Widget for Video<'a> { .lossy_terrain_compression != lossy_terrain_compression { - events.push(GraphicsChange::ToggleLossyTerrainCompression( - lossy_terrain_compression, - )); + events.push(ToggleLossyTerrainCompression(lossy_terrain_compression)); } // Resolution @@ -1045,7 +946,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.resolution_label, 10.0) .set(state.ids.resolution, ui) { - events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings { + events.push(ChangeFullscreenMode(FullScreenSettings { resolution: resolutions[clicked], ..self.global_state.settings.graphics.fullscreen })); @@ -1109,7 +1010,7 @@ impl<'a> Widget for Video<'a> { .right_from(state.ids.resolution, 8.0) .set(state.ids.bit_depth, ui) { - events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings { + events.push(ChangeFullscreenMode(FullScreenSettings { bit_depth: if clicked == 0 { None } else { @@ -1163,7 +1064,7 @@ impl<'a> Widget for Video<'a> { .right_from(state.ids.bit_depth, 8.0) .set(state.ids.refresh_rate, ui) { - events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings { + events.push(ChangeFullscreenMode(FullScreenSettings { refresh_rate: if clicked == 0 { None } else { @@ -1193,7 +1094,7 @@ impl<'a> Widget for Video<'a> { .set(state.ids.fullscreen_button, ui); if self.global_state.settings.graphics.fullscreen.enabled != enabled { - events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings { + events.push(ChangeFullscreenMode(FullScreenSettings { enabled, ..self.global_state.settings.graphics.fullscreen })); @@ -1230,7 +1131,7 @@ impl<'a> Widget for Video<'a> { .down_from(state.ids.fullscreen_mode_text, 8.0) .set(state.ids.fullscreen_mode_list, ui) { - events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings { + events.push(ChangeFullscreenMode(FullScreenSettings { mode: mode_list[clicked], ..self.global_state.settings.graphics.fullscreen })); @@ -1250,7 +1151,7 @@ impl<'a> Widget for Video<'a> { .set(state.ids.save_window_size_button, ui) .was_clicked() { - events.push(GraphicsChange::AdjustWindowSize( + events.push(AdjustWindowSize( self.global_state .window .logical_size() @@ -1274,7 +1175,7 @@ impl<'a> Widget for Video<'a> { .set(state.ids.reset_graphics_button, ui) .was_clicked() { - events.push(GraphicsChange::ResetGraphicsSettings); + events.push(ResetGraphicsSettings); } events diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 4d2a1a04dd..95a38fb3c0 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -2,14 +2,7 @@ #![allow(incomplete_features)] #![allow(clippy::option_map_unit_fn)] #![deny(clippy::clone_on_ref_ptr)] -#![feature( - array_map, - bool_to_option, - const_generics, - drain_filter, - once_cell, - trait_alias -)] +#![feature(array_map, bool_to_option, const_generics, drain_filter, once_cell)] #![recursion_limit = "2048"] #[macro_use] diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 4b8093bc8e..23290cee2e 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -20,7 +20,6 @@ pub struct CharSelectionState { char_selection_ui: CharSelectionUi, client: Rc>, scene: Scene, - need_shadow_clear: bool, } impl CharSelectionState { @@ -37,7 +36,6 @@ impl CharSelectionState { char_selection_ui, client, scene, - need_shadow_clear: false, } } @@ -73,9 +71,6 @@ impl PlayState for CharSelectionState { // Set scale mode in case it was change self.char_selection_ui .set_scale_mode(global_state.settings.interface.ui_scale); - - // Clear shadow textures since we don't render to them here - self.need_shadow_clear = true; } fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { @@ -235,39 +230,15 @@ impl PlayState for CharSelectionState { fn capped_fps(&self) -> bool { true } fn render(&mut self, renderer: &mut Renderer, _: &Settings) { - let mut drawer = match renderer - .start_recording_frame(self.scene.global_bind_group()) - .expect("Unrecoverable render error when starting a new frame!") - { - Some(d) => d, - // Couldn't get swap chain texture this fime - None => return, - }; - - if self.need_shadow_clear { - drawer.clear_shadows(); - self.need_shadow_clear = false; - } - let client = self.client.borrow(); let (humanoid_body, loadout) = Self::get_humanoid_body_inventory(&self.char_selection_ui, &client); - if let Some(mut first_pass) = drawer.first_pass() { - self.scene - .render(&mut first_pass, client.get_tick(), humanoid_body, loadout); - } + // Render the scene. + self.scene + .render(renderer, client.get_tick(), humanoid_body, loadout); - // Clouds - if let Some(mut second_pass) = drawer.second_pass() { - second_pass.draw_clouds(); - } - // PostProcess and UI - let mut third_pass = drawer.third_pass(); - third_pass.draw_postprocess(); // Draw the UI to the screen. - if let Some(mut ui_drawer) = third_pass.draw_ui() { - self.char_selection_ui.render(&mut ui_drawer); - }; + self.char_selection_ui.render(renderer); } } diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index cc1c853910..0ce849a58e 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1,6 +1,6 @@ use crate::{ i18n::{Localization, LocalizationHandle}, - render::UiDrawer, + render::Renderer, ui::{ self, fonts::IcedFonts as Fonts, @@ -1584,7 +1584,8 @@ impl CharSelectionUi { events } - pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.ui.render(drawer); } + // TODO: do we need globals? + pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); } } #[derive(Default)] diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index ca4bd328a4..76d93b2112 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -1,5 +1,4 @@ mod client_init; -mod scene; mod ui; use super::char_selection::CharSelectionState; @@ -12,41 +11,20 @@ use crate::{ use client::{ addr::ConnectionArgs, error::{InitProtocolError, NetworkConnectError, NetworkError}, - Client, ServerInfo, + ServerInfo, }; use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; use common::comp; use common_base::span; -use scene::Scene; use std::sync::Arc; use tokio::runtime; use tracing::error; use ui::{Event as MainMenuEvent, MainMenuUi}; -// TODO: show status messages for waiting on server creation, client init, and -// pipeline creation (we can show progress of pipeline creation) -enum InitState { - None, - // Waiting on the client initialization - Client(ClientInit), - // Client initialized but still waiting on Renderer pipeline creation - Pipeline(Box), -} - -impl InitState { - fn client(&self) -> Option<&ClientInit> { - if let Self::Client(client_init) = &self { - Some(client_init) - } else { - None - } - } -} - pub struct MainMenuState { main_menu_ui: MainMenuUi, - init: InitState, - scene: Scene, + // Used for client creation. + client_init: Option, } impl MainMenuState { @@ -54,8 +32,7 @@ impl MainMenuState { pub fn new(global_state: &mut GlobalState) -> Self { Self { main_menu_ui: MainMenuUi::new(global_state), - init: InitState::None, - scene: Scene::new(global_state.window.renderer_mut()), + client_init: None, } } } @@ -97,14 +74,14 @@ impl PlayState for MainMenuState { "singleplayer".to_owned(), "".to_owned(), ConnectionArgs::Mpsc(14004), - &mut self.init, + &mut self.client_init, Some(runtime), ); }, Ok(Err(e)) => { error!(?e, "Could not start server"); global_state.singleplayer = None; - self.init = InitState::None; + self.client_init = None; self.main_menu_ui.cancel_connection(); self.main_menu_ui.show_info(format!("Error: {:?}", e)); }, @@ -127,14 +104,19 @@ impl PlayState for MainMenuState { } } // Poll client creation. - match self.init.client().and_then(|init| init.poll()) { + match self.client_init.as_ref().and_then(|init| init.poll()) { Some(InitMsg::Done(Ok(mut client))) => { + self.client_init = None; + self.main_menu_ui.connected(); // Register voxygen components / resources crate::ecs::init(client.state_mut().ecs_mut()); - self.init = InitState::Pipeline(Box::new(client)); + return PlayStateResult::Push(Box::new(CharSelectionState::new( + global_state, + std::rc::Rc::new(std::cell::RefCell::new(client)), + ))); }, Some(InitMsg::Done(Err(e))) => { - self.init = InitState::None; + self.client_init = None; tracing::trace!(?e, "raw Client Init error"); let e = get_client_msg_error(e, &global_state.i18n); // Log error for possible additional use later or incase that the error @@ -150,7 +132,10 @@ impl PlayState for MainMenuState { .contains(&auth_server) { // Can't fail since we just polled it, it must be Some - self.init.client().unwrap().auth_trust(auth_server, true); + self.client_init + .as_ref() + .unwrap() + .auth_trust(auth_server, true); } else { // Show warning that auth server is not trusted and prompt for approval self.main_menu_ui.auth_trust_prompt(auth_server); @@ -159,64 +144,6 @@ impl PlayState for MainMenuState { None => {}, } - // Tick the client to keep the connection alive if we are waiting on pipelines - let localized_strings = &global_state.i18n.read(); - if let InitState::Pipeline(client) = &mut self.init { - match client.tick( - comp::ControllerInputs::default(), - global_state.clock.dt(), - |_| {}, - ) { - Ok(events) => { - for event in events { - match event { - client::Event::SetViewDistance(vd) => { - global_state.settings.graphics.view_distance = vd; - global_state.settings.save_to_file_warn(); - }, - client::Event::Disconnect => { - global_state.info_message = Some( - localized_strings - .get("main.login.server_shut_down") - .to_owned(), - ); - self.init = InitState::None; - }, - _ => {}, - } - } - }, - Err(err) => { - global_state.info_message = - Some(localized_strings.get("common.connection_lost").to_owned()); - error!(?err, "[main menu] Failed to tick the client"); - self.init = InitState::None; - }, - } - } - - // Poll renderer pipeline creation - if let InitState::Pipeline(..) = &self.init { - // If not complete go to char select screen - if global_state - .window - .renderer() - .pipeline_creation_status() - .is_none() - { - // Always succeeds since we check above - if let InitState::Pipeline(client) = - core::mem::replace(&mut self.init, InitState::None) - { - self.main_menu_ui.connected(); - return PlayStateResult::Push(Box::new(CharSelectionState::new( - global_state, - std::rc::Rc::new(std::cell::RefCell::new(*client)), - ))); - } - } - } - // Maintain the UI. for event in self .main_menu_ui @@ -253,19 +180,19 @@ impl PlayState for MainMenuState { username, password, connection_args, - &mut self.init, + &mut self.client_init, None, ); }, MainMenuEvent::CancelLoginAttempt => { - // init contains InitState::Client(ClientInit), which spawns a thread which - // contains a TcpStream::connect() call This call is - // blocking TODO fix when the network rework happens + // client_init contains Some(ClientInit), which spawns a thread which contains a + // TcpStream::connect() call This call is blocking + // TODO fix when the network rework happens #[cfg(feature = "singleplayer")] { global_state.singleplayer = None; } - self.init = InitState::None; + self.client_init = None; self.main_menu_ui.cancel_connection(); }, MainMenuEvent::ChangeLanguage(new_language) => { @@ -301,8 +228,8 @@ impl PlayState for MainMenuState { .insert(auth_server.clone()); global_state.settings.save_to_file_warn(); } - self.init - .client() + self.client_init + .as_ref() .map(|init| init.auth_trust(auth_server, trust)); }, } @@ -320,19 +247,8 @@ impl PlayState for MainMenuState { fn capped_fps(&self) -> bool { true } fn render(&mut self, renderer: &mut Renderer, _: &Settings) { - let mut drawer = match renderer - .start_recording_frame(self.scene.global_bind_group()) - .expect("Unrecoverable render error when starting a new frame!") - { - Some(d) => d, - // Couldn't get swap chain texture this frame - None => return, - }; - // Draw the UI to the screen. - if let Some(mut ui_drawer) = drawer.third_pass().draw_ui() { - self.main_menu_ui.render(&mut ui_drawer); - }; + self.main_menu_ui.render(renderer); } } @@ -440,7 +356,7 @@ fn attempt_login( username: String, password: String, connection_args: ConnectionArgs, - init: &mut InitState, + client_init: &mut Option, runtime: Option>, ) { if let Err(err) = comp::Player::alias_validate(&username) { @@ -449,8 +365,8 @@ fn attempt_login( } // Don't try to connect if there is already a connection in progress. - if let InitState::None = init { - *init = InitState::Client(ClientInit::new( + if client_init.is_none() { + *client_init = Some(ClientInit::new( connection_args, username, password, diff --git a/voxygen/src/menu/main/scene.rs b/voxygen/src/menu/main/scene.rs deleted file mode 100644 index 77f5c709d0..0000000000 --- a/voxygen/src/menu/main/scene.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::render::{ - GlobalModel, Globals, GlobalsBindGroup, Light, LodData, PointLightMatrix, Renderer, Shadow, - ShadowLocals, -}; - -pub struct Scene { - bind_group: GlobalsBindGroup, -} - -impl Scene { - pub fn new(renderer: &mut Renderer) -> Self { - let global_data = GlobalModel { - globals: renderer.create_consts(&[Globals::default()]), - lights: renderer.create_consts(&[Light::default(); 32]), - shadows: renderer.create_consts(&[Shadow::default(); 32]), - shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]), - point_light_matrices: Box::new([PointLightMatrix::default(); 126]), - }; - - let lod_data = LodData::dummy(renderer); - - let bind_group = renderer.bind_globals(&global_data, &lod_data); - - Self { bind_group } - } - - pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.bind_group } -} diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index 2df020dd57..34de2acd5f 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -6,7 +6,7 @@ mod servers; use crate::{ i18n::{LanguageMetadata, LocalizationHandle}, - render::UiDrawer, + render::Renderer, ui::{ self, fonts::IcedFonts as Fonts, @@ -477,7 +477,7 @@ pub struct MainMenuUi { controls: Controls, } -impl MainMenuUi { +impl<'a> MainMenuUi { pub fn new(global_state: &mut GlobalState) -> Self { // Load language let i18n = &global_state.i18n.read(); @@ -583,5 +583,5 @@ impl MainMenuUi { events } - pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.ui.render(drawer); } + pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); } } diff --git a/voxygen/src/mesh/greedy.rs b/voxygen/src/mesh/greedy.rs index ef8f4f5014..f39094fb7a 100644 --- a/voxygen/src/mesh/greedy.rs +++ b/voxygen/src/mesh/greedy.rs @@ -1,7 +1,9 @@ -use crate::render::{mesh::Quad, ColLightInfo, TerrainVertex, Vertex}; +use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline}; use common_base::span; use vek::*; +type TerrainVertex = ::Vertex; + type TodoRect = ( Vec3, Vec2>, @@ -121,7 +123,7 @@ impl<'a> GreedyMesh<'a> { small_size_threshold, large_size_threshold, }); - let col_lights_size = Vec2::new(1, 1); + let col_lights_size = Vec2::new(1u16, 1u16); Self { atlas, col_lights_size, @@ -150,7 +152,7 @@ impl<'a> GreedyMesh<'a> { FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), - FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8) -> [u8; 4] + 'a, + FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8) -> <::Surface as gfx::format::SurfaceTyped>::DataType + 'a, { span!(_guard, "push", "GreedyMesh::push"); let cont = greedy_mesh( @@ -176,7 +178,7 @@ impl<'a> GreedyMesh<'a> { let cur_size = self.col_lights_size; let col_lights = vec![ TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254)); - cur_size.x as usize * cur_size.y as usize + usize::from(cur_size.x) * usize::from(cur_size.y) ]; let mut col_lights_info = (col_lights, cur_size); self.suspended.into_iter().for_each(|cont| { @@ -211,7 +213,7 @@ where FO: for<'r> FnMut(&'r mut D, Vec3) -> bool + 'a, FS: for<'r> FnMut(&'r mut D, Vec3, Vec3, Vec2>) -> Option<(bool, M)>, FP: FnMut(Vec2, Vec2>, Vec3, Vec2>, Vec3, &M), - FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8) -> [u8; 4] + 'a, + FT: for<'r> FnMut(&'r mut D, Vec3, u8, u8) -> <::Surface as gfx::format::SurfaceTyped>::DataType + 'a, { span!(_guard, "greedy_mesh"); // TODO: Collect information to see if we can choose a good value here. @@ -505,7 +507,7 @@ fn draw_col_lights( mut get_light: impl FnMut(&mut D, Vec3) -> f32, mut get_glow: impl FnMut(&mut D, Vec3) -> f32, mut get_opacity: impl FnMut(&mut D, Vec3) -> bool, - mut make_face_texel: impl FnMut(&mut D, Vec3, u8, u8) -> [u8; 4], + mut make_face_texel: impl FnMut(&mut D, Vec3, u8, u8) -> <::Surface as gfx::format::SurfaceTyped>::DataType, ) { todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| { // NOTE: Conversions are safe because width, height, and offset must be @@ -518,7 +520,7 @@ fn draw_col_lights( let uv = uv.map(|e| e.map(i32::from)); let pos = pos + draw_delta; (0..height).for_each(|v| { - let start = cur_size.x as usize * usize::from(top + v) + usize::from(left); + let start = usize::from(cur_size.x) * usize::from(top + v) + usize::from(left); (0..width) .zip(&mut col_lights[start..start + usize::from(width)]) .for_each(|(u, col_light)| { @@ -620,14 +622,14 @@ fn create_quad_greedy( push_quad(atlas_pos, dim, origin, draw_dim, norm, meta); } -pub fn create_quad( +pub fn create_quad( atlas_pos: Vec2, dim: Vec2>, origin: Vec3, draw_dim: Vec2>, norm: Vec3, meta: &M, - create_vertex: impl Fn(Vec2, Vec3, Vec3, &M) -> O, + create_vertex: impl Fn(Vec2, Vec3, Vec3, &M) -> O::Vertex, ) -> Quad { Quad::new( create_vertex(atlas_pos, origin, norm, meta), diff --git a/voxygen/src/mesh/mod.rs b/voxygen/src/mesh/mod.rs index b8c1ed8af9..657d635e32 100644 --- a/voxygen/src/mesh/mod.rs +++ b/voxygen/src/mesh/mod.rs @@ -2,6 +2,25 @@ pub mod greedy; pub mod segment; pub mod terrain; -use crate::render::Mesh; +use crate::render::{self, Mesh}; -pub type MeshGen = (Mesh, Mesh, Mesh, R); +pub type MeshGen = ( + Mesh<>::Pipeline>, + Mesh<>::TranslucentPipeline>, + Mesh<>::ShadowPipeline>, + >::Result, +); + +/// FIXME: Remove this whole trait at some point. This "abstraction" is never +/// abstracted over, and is organized completely differently from how we +/// actually mesh things nowadays. +pub trait Meshable { + type Pipeline: render::Pipeline; + type TranslucentPipeline: render::Pipeline; + type ShadowPipeline: render::Pipeline; + type Supplement; + type Result; + + // Generate meshes - one opaque, one translucent, one shadow + fn generate_mesh(self, supp: Self::Supplement) -> MeshGen; +} diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index 0de1b21701..eca4cc9d62 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -1,9 +1,12 @@ use crate::{ mesh::{ greedy::{self, GreedyConfig, GreedyMesh}, - MeshGen, + MeshGen, Meshable, + }, + render::{ + self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline, + TerrainPipeline, }, - render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex}, scene::math, }; use common::{ @@ -13,315 +16,353 @@ use common::{ use core::convert::TryFrom; use vek::*; -// /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some -// /// point). -#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 -// TODO: this function name... -pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>( - vol: V, - (greedy, opaque_mesh, offs, scale, bone_idx): ( +type SpriteVertex = ::Vertex; +type TerrainVertex = ::Vertex; +type ParticleVertex = ::Vertex; + +impl<'a: 'b, 'b, V: 'a> Meshable> for V +where + V: BaseVol + ReadVol + SizedVol, + /* TODO: Use VolIterator instead of manually iterating + * &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>, + * &'a V: BaseVol, */ +{ + type Pipeline = TerrainPipeline; + type Result = math::Aabb; + type ShadowPipeline = ShadowPipeline; + /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some + /// point). + type Supplement = ( &'b mut GreedyMesh<'a>, - &'b mut Mesh, + &'b mut Mesh, Vec3, Vec3, u8, - ), -) -> MeshGen> -where - V: BaseVol + ReadVol + SizedVol, -{ - assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]"); - let max_size = greedy.max_size(); - // NOTE: Required because we steal two bits from the normal in the shadow uint - // in order to store the bone index. The two bits are instead taken out - // of the atlas coordinates, which is why we "only" allow 1 << 15 per - // coordinate instead of 1 << 16. - assert!(max_size.width.max(max_size.height) < 1 << 15); - - let lower_bound = vol.lower_bound(); - let upper_bound = vol.upper_bound(); - assert!( - lower_bound.x <= upper_bound.x - && lower_bound.y <= upper_bound.y - && lower_bound.z <= upper_bound.z ); - // NOTE: Figure sizes should be no more than 512 along each axis. - let greedy_size = upper_bound - lower_bound + 1; - assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512); - // NOTE: Cast to usize is safe because of previous check, since all values fit - // into u16 which is safe to cast to usize. - let greedy_size = greedy_size.as_::(); - let greedy_size_cross = greedy_size; - let draw_delta = lower_bound; + type TranslucentPipeline = FigurePipeline; - let get_light = |vol: &mut V, pos: Vec3| { - if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { - 1.0 - } else { - 0.0 + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 + fn generate_mesh( + self, + (greedy, opaque_mesh, offs, scale, bone_idx): Self::Supplement, + ) -> MeshGen, Self> { + assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]"); + + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 15); + + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + // NOTE: Figure sizes should be no more than 512 along each axis. + let greedy_size = upper_bound - lower_bound + 1; + assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; + + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 + } + }; + let get_glow = |_vol: &mut V, _pos: Vec3| 0.0; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map_or(true, |vox| vox.is_empty()); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy(pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) + }) + }; + let create_opaque = |atlas_pos, pos, norm| { + TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx) + }; + + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_glow, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + make_face_texel: |vol: &mut V, pos, light, _| { + let cell = vol.get(pos).ok(); + let (glowy, shiny) = cell + .map(|c| (c.is_glowy(), c.is_shiny())) + .unwrap_or_default(); + let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero()); + TerrainVertex::make_col_light_figure(light, glowy, shiny, col) + }, + }); + let bounds = math::Aabb { + // NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16. + min: math::Vec3::from((lower_bound.as_::() + offs) * scale), + max: math::Vec3::from((upper_bound.as_::() + offs) * scale), } - }; - let get_glow = |_vol: &mut V, _pos: Vec3| 0.0; - let get_opacity = |vol: &mut V, pos: Vec3| vol.get(pos).map_or(true, |vox| vox.is_empty()); - let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { - should_draw_greedy(pos, delta, uv, |vox| { - vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) - }) - }; - let create_opaque = |atlas_pos, pos, norm| { - TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx) - }; + .made_valid(); - greedy.push(GreedyConfig { - data: vol, - draw_delta, - greedy_size, - greedy_size_cross, - get_light, - get_glow, - get_opacity, - should_draw, - push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { - opaque_mesh.push_quad(greedy::create_quad( - atlas_origin, - dim, - origin, - draw_dim, - norm, - meta, - |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), - )); - }, - make_face_texel: |vol: &mut V, pos, light, _| { - let cell = vol.get(pos).ok(); - let (glowy, shiny) = cell - .map(|c| (c.is_glowy(), c.is_shiny())) - .unwrap_or_default(); - let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero()); - TerrainVertex::make_col_light_figure(light, glowy, shiny, col) - }, - }); - let bounds = math::Aabb { - // NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16. - min: math::Vec3::from((lower_bound.as_::() + offs) * scale), - max: math::Vec3::from((upper_bound.as_::() + offs) * scale), + (Mesh::new(), Mesh::new(), Mesh::new(), bounds) } - .made_valid(); - - (Mesh::new(), Mesh::new(), Mesh::new(), bounds) } -#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 -pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>( - vol: V, - (greedy, opaque_mesh, vertical_stripes): ( - &'b mut GreedyMesh<'a>, - &'b mut Mesh, - bool, - ), -) -> MeshGen +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, + /* TODO: Use VolIterator instead of manually iterating + * &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>, + * &'a V: BaseVol, */ { - let max_size = greedy.max_size(); - // NOTE: Required because we steal two bits from the normal in the shadow uint - // in order to store the bone index. The two bits are instead taken out - // of the atlas coordinates, which is why we "only" allow 1 << 15 per - // coordinate instead of 1 << 16. - assert!(max_size.width.max(max_size.height) < 1 << 16); + type Pipeline = SpritePipeline; + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh, bool); + type TranslucentPipeline = SpritePipeline; - let lower_bound = vol.lower_bound(); - let upper_bound = vol.upper_bound(); - assert!( - lower_bound.x <= upper_bound.x - && lower_bound.y <= upper_bound.y - && lower_bound.z <= upper_bound.z - ); - // Lower bound coordinates must fit in an i16 (which means upper bound - // coordinates fit as integers in a f23). - assert!( - i16::try_from(lower_bound.x).is_ok() - && i16::try_from(lower_bound.y).is_ok() - && i16::try_from(lower_bound.z).is_ok(), - "Sprite offsets should fit in i16", - ); - let greedy_size = upper_bound - lower_bound + 1; - // TODO: Should this be 16, 16, 64? - assert!( - greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64, - "Sprite size out of bounds: {:?} ≤ (31, 31, 63)", - greedy_size - 1 - ); + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 + fn generate_mesh( + self, + (greedy, opaque_mesh, vertical_stripes): Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let (flat, flat_get) = { - let (w, h, d) = (greedy_size + 2).into_tuple(); - let flat = { - let mut flat = vec![Cell::empty(); (w * h * d) as usize]; - let mut i = 0; - for x in -1..greedy_size.x + 1 { - for y in -1..greedy_size.y + 1 { - for z in -1..greedy_size.z + 1 { - let wpos = lower_bound + Vec3::new(x, y, z); - let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty()); - flat[i] = block; - i += 1; + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + // Lower bound coordinates must fit in an i16 (which means upper bound + // coordinates fit as integers in a f23). + assert!( + i16::try_from(lower_bound.x).is_ok() + && i16::try_from(lower_bound.y).is_ok() + && i16::try_from(lower_bound.z).is_ok(), + "Sprite offsets should fit in i16", + ); + let greedy_size = upper_bound - lower_bound + 1; + // TODO: Should this be 16, 16, 64? + assert!( + greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64, + "Sprite size out of bounds: {:?} ≤ (31, 31, 63)", + greedy_size - 1 + ); + + let (flat, flat_get) = { + let (w, h, d) = (greedy_size + 2).into_tuple(); + let flat = { + let vol = self; + + let mut flat = vec![Cell::empty(); (w * h * d) as usize]; + let mut i = 0; + for x in -1..greedy_size.x + 1 { + for y in -1..greedy_size.y + 1 { + for z in -1..greedy_size.z + 1 { + let wpos = lower_bound + Vec3::new(x, y, z); + let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty()); + flat[i] = block; + i += 1; + } } } + flat + }; + + let flat_get = move |flat: &Vec, Vec3 { x, y, z }| match flat + .get((x * h * d + y * d + z) as usize) + .copied() + { + Some(b) => b, + None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), + }; + + (flat, flat_get) + }; + + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); + + let greedy_size_cross = greedy_size; + let draw_delta = Vec3::new(1, 1, 1); + + let get_light = move |flat: &mut _, pos: Vec3| { + if flat_get(flat, pos).is_empty() { + 1.0 + } else { + 0.0 } - flat + }; + let get_glow = |_flat: &mut _, _pos: Vec3| 0.0; + let get_color = move |flat: &mut _, pos: Vec3| { + flat_get(flat, pos).get_color().unwrap_or(Rgb::zero()) + }; + let get_opacity = move |flat: &mut _, pos: Vec3| flat_get(flat, pos).is_empty(); + let should_draw = move |flat: &mut _, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox)) + }; + // NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final + // position, pos + mesh_delta, is guaranteed to fit in an f32). + let mesh_delta = lower_bound.as_::(); + let create_opaque = |atlas_pos, pos: Vec3, norm, _meta| { + SpriteVertex::new(atlas_pos, pos + mesh_delta, norm) }; - let flat_get = move |flat: &Vec, Vec3 { x, y, z }| match flat - .get((x * h * d + y * d + z) as usize) - .copied() - { - Some(b) => b, - None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), - }; + greedy.push(GreedyConfig { + data: flat, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_glow, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + make_face_texel: move |flat: &mut _, pos, light, glow| { + TerrainVertex::make_col_light(light, glow, get_color(flat, pos)) + }, + }); - (flat, flat_get) - }; - - // NOTE: Cast to usize is safe because of previous check, since all values fit - // into u16 which is safe to cast to usize. - let greedy_size = greedy_size.as_::(); - - let greedy_size_cross = greedy_size; - let draw_delta = Vec3::new(1, 1, 1); - - let get_light = move |flat: &mut _, pos: Vec3| { - if flat_get(flat, pos).is_empty() { - 1.0 - } else { - 0.0 - } - }; - let get_glow = |_flat: &mut _, _pos: Vec3| 0.0; - let get_color = - move |flat: &mut _, pos: Vec3| flat_get(flat, pos).get_color().unwrap_or(Rgb::zero()); - let get_opacity = move |flat: &mut _, pos: Vec3| flat_get(flat, pos).is_empty(); - let should_draw = move |flat: &mut _, pos: Vec3, delta: Vec3, uv| { - should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox)) - }; - // NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final - // position, pos + mesh_delta, is guaranteed to fit in an f32). - let mesh_delta = lower_bound.as_::(); - let create_opaque = |atlas_pos, pos: Vec3, norm, _meta| { - SpriteVertex::new(atlas_pos, pos + mesh_delta, norm) - }; - - greedy.push(GreedyConfig { - data: flat, - draw_delta, - greedy_size, - greedy_size_cross, - get_light, - get_glow, - get_opacity, - should_draw, - push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| { - opaque_mesh.push_quad(greedy::create_quad( - atlas_origin, - dim, - origin, - draw_dim, - norm, - meta, - |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), - )); - }, - make_face_texel: move |flat: &mut _, pos, light, glow| { - TerrainVertex::make_col_light(light, glow, get_color(flat, pos)) - }, - }); - - (Mesh::new(), Mesh::new(), Mesh::new(), ()) + (Mesh::new(), Mesh::new(), Mesh::new(), ()) + } } -#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 -pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>( - vol: V, - greedy: &'b mut GreedyMesh<'a>, -) -> MeshGen +impl<'a: 'b, 'b, V: 'a> Meshable> for V where V: BaseVol + ReadVol + SizedVol, + /* TODO: Use VolIterator instead of manually iterating + * &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>, + * &'a V: BaseVol, */ { - let max_size = greedy.max_size(); - // NOTE: Required because we steal two bits from the normal in the shadow uint - // in order to store the bone index. The two bits are instead taken out - // of the atlas coordinates, which is why we "only" allow 1 << 15 per - // coordinate instead of 1 << 16. - assert!(max_size.width.max(max_size.height) < 1 << 16); + type Pipeline = ParticlePipeline; + type Result = (); + type ShadowPipeline = ShadowPipeline; + type Supplement = &'b mut GreedyMesh<'a>; + type TranslucentPipeline = ParticlePipeline; - let lower_bound = vol.lower_bound(); - let upper_bound = vol.upper_bound(); - assert!( - lower_bound.x <= upper_bound.x - && lower_bound.y <= upper_bound.y - && lower_bound.z <= upper_bound.z - ); - let greedy_size = upper_bound - lower_bound + 1; - assert!( - greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, - "Particle size out of bounds: {:?} ≤ (15, 15, 63)", - greedy_size - 1 - ); - // NOTE: Cast to usize is safe because of previous check, since all values fit - // into u16 which is safe to cast to usize. - let greedy_size = greedy_size.as_::(); + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 + fn generate_mesh( + self, + greedy: Self::Supplement, + ) -> MeshGen, Self> { + let max_size = greedy.max_size(); + // NOTE: Required because we steal two bits from the normal in the shadow uint + // in order to store the bone index. The two bits are instead taken out + // of the atlas coordinates, which is why we "only" allow 1 << 15 per + // coordinate instead of 1 << 16. + assert!(max_size.width.max(max_size.height) < 1 << 16); - let greedy_size_cross = greedy_size; - let draw_delta = lower_bound; + let lower_bound = self.lower_bound(); + let upper_bound = self.upper_bound(); + assert!( + lower_bound.x <= upper_bound.x + && lower_bound.y <= upper_bound.y + && lower_bound.z <= upper_bound.z + ); + let greedy_size = upper_bound - lower_bound + 1; + assert!( + greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64, + "Particle size out of bounds: {:?} ≤ (15, 15, 63)", + greedy_size - 1 + ); + // NOTE: Cast to usize is safe because of previous check, since all values fit + // into u16 which is safe to cast to usize. + let greedy_size = greedy_size.as_::(); - let get_light = |vol: &mut V, pos: Vec3| { - if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { - 1.0 - } else { - 0.0 - } - }; - let get_glow = |_vol: &mut V, _pos: Vec3| 0.0; - let get_color = |vol: &mut V, pos: Vec3| { - vol.get(pos) - .ok() - .and_then(|vox| vox.get_color()) - .unwrap_or(Rgb::zero()) - }; - let get_opacity = |vol: &mut V, pos: Vec3| vol.get(pos).map_or(true, |vox| vox.is_empty()); - let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { - should_draw_greedy(pos, delta, uv, |vox| { - vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) - }) - }; - let create_opaque = |_atlas_pos, pos: Vec3, norm| ParticleVertex::new(pos, norm); + let greedy_size_cross = greedy_size; + let draw_delta = lower_bound; - let mut opaque_mesh = Mesh::new(); - greedy.push(GreedyConfig { - data: vol, - draw_delta, - greedy_size, - greedy_size_cross, - get_light, - get_glow, - get_opacity, - should_draw, - push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { - opaque_mesh.push_quad(greedy::create_quad( - atlas_origin, - dim, - origin, - draw_dim, - norm, - meta, - |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), - )); - }, - make_face_texel: move |vol: &mut V, pos, light, glow| { - TerrainVertex::make_col_light(light, glow, get_color(vol, pos)) - }, - }); + let get_light = |vol: &mut V, pos: Vec3| { + if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) { + 1.0 + } else { + 0.0 + } + }; + let get_glow = |_vol: &mut V, _pos: Vec3| 0.0; + let get_color = |vol: &mut V, pos: Vec3| { + vol.get(pos) + .ok() + .and_then(|vox| vox.get_color()) + .unwrap_or(Rgb::zero()) + }; + let get_opacity = + |vol: &mut V, pos: Vec3| vol.get(pos).map_or(true, |vox| vox.is_empty()); + let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, uv| { + should_draw_greedy(pos, delta, uv, |vox| { + vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) + }) + }; + let create_opaque = |_atlas_pos, pos: Vec3, norm| ParticleVertex::new(pos, norm); - (opaque_mesh, Mesh::new(), Mesh::new(), ()) + let mut opaque_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: self, + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_glow, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm), + )); + }, + make_face_texel: move |vol: &mut V, pos, light, glow| { + TerrainVertex::make_col_light(light, glow, get_color(vol, pos)) + }, + }); + + (opaque_mesh, Mesh::new(), Mesh::new(), ()) + } } fn should_draw_greedy( diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index b90f5e440b..682e798dd6 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -3,9 +3,9 @@ use crate::{ mesh::{ greedy::{self, GreedyConfig, GreedyMesh}, - MeshGen, + MeshGen, Meshable, }, - render::{ColLightInfo, FluidVertex, Mesh, TerrainVertex}, + render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, scene::terrain::BlocksOfInterest, }; use common::{ @@ -19,6 +19,9 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc}; use tracing::error; use vek::*; +type TerrainVertex = ::Vertex; +type FluidVertex = ::Vertex; + #[derive(Clone, Copy, PartialEq)] enum FaceKind { /// Opaque face that is facing something non-opaque; either @@ -224,234 +227,243 @@ fn calc_light + ReadVol + Debug>( } } -#[allow(clippy::collapsible_if)] -#[allow(clippy::many_single_char_names)] -#[allow(clippy::type_complexity)] -#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 -#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 -pub fn generate_mesh<'a, V: RectRasterableVol + ReadVol + Debug + 'static>( - vol: &'a VolGrid2d, - (range, max_texture_size, _boi): (Aabb, Vec2, &'a BlocksOfInterest), -) -> MeshGen< - TerrainVertex, - FluidVertex, - TerrainVertex, - ( +impl<'a, V: RectRasterableVol + ReadVol + Debug + 'static> + Meshable for &'a VolGrid2d +{ + type Pipeline = TerrainPipeline; + #[allow(clippy::type_complexity)] + type Result = ( Aabb, ColLightInfo, Arc) -> f32 + Send + Sync>, Arc) -> f32 + Send + Sync>, - ), -> { - span!( - _guard, - "generate_mesh", - "<&VolGrid2d as Meshable<_, _>>::generate_mesh" ); + type ShadowPipeline = ShadowPipeline; + type Supplement = (Aabb, Vec2, &'a BlocksOfInterest); + type TranslucentPipeline = FluidPipeline; - // Find blocks that should glow - // TODO: Search neighbouring chunks too! - // let glow_blocks = boi.lights - // .iter() - // .map(|(pos, glow)| (*pos + range.min.xy(), *glow)); - /* DefaultVolIterator::new(vol, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) - .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */ + #[allow(clippy::collapsible_if)] + #[allow(clippy::many_single_char_names)] + #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587 + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 + fn generate_mesh( + self, + (range, max_texture_size, _boi): Self::Supplement, + ) -> MeshGen { + span!( + _guard, + "generate_mesh", + "<&VolGrid2d as Meshable<_, _>>::generate_mesh" + ); - let mut glow_blocks = Vec::new(); + // Find blocks that should glow + // TODO: Search neighbouring chunks too! + // let glow_blocks = boi.lights + // .iter() + // .map(|(pos, glow)| (*pos + range.min.xy(), *glow)); + /* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) + .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */ - // TODO: This expensive, use BlocksOfInterest instead - let mut volume = vol.cached(); - for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST { - for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST { - for z in -1..range.size().d + 1 { - let wpos = range.min + Vec3::new(x, y, z); - volume - .get(wpos) - .ok() - .and_then(|b| b.get_glow()) - .map(|glow| glow_blocks.push((wpos, glow))); - } - } - } + let mut glow_blocks = Vec::new(); - // Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0) - let light = calc_light(true, SUNLIGHT, range, vol, core::iter::empty()); - let glow = calc_light(false, 0, range, vol, glow_blocks.into_iter()); - - let mut opaque_limits = None::; - let mut fluid_limits = None::; - let mut air_limits = None::; - let flat_get = { - span!(_guard, "copy to flat array"); - let (w, h, d) = range.size().into_tuple(); - // z can range from -1..range.size().d + 1 - let d = d + 2; - let flat = { - let mut volume = vol.cached(); - const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty); - // TODO: Once we can manage it sensibly, consider using something like - // Option instead of just assuming air. - let mut flat = vec![AIR; (w * h * d) as usize]; - let mut i = 0; - for x in 0..range.size().w { - for y in 0..range.size().h { - for z in -1..range.size().d + 1 { - let wpos = range.min + Vec3::new(x, y, z); - let block = volume - .get(wpos) - .map(|b| *b) - // TODO: Replace with None or some other more reasonable value, - // since it's not clear this will work properly with liquid. - .unwrap_or(AIR); - if block.is_opaque() { - opaque_limits = opaque_limits - .map(|l| l.including(z)) - .or_else(|| Some(Limits::from_value(z))); - } else if block.is_liquid() { - fluid_limits = fluid_limits - .map(|l| l.including(z)) - .or_else(|| Some(Limits::from_value(z))); - } else { - // Assume air - air_limits = air_limits - .map(|l| l.including(z)) - .or_else(|| Some(Limits::from_value(z))); - }; - flat[i] = block; - i += 1; - } + // TODO: This expensive, use BlocksOfInterest instead + let mut volume = self.cached(); + for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST { + for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST { + for z in -1..range.size().d + 1 { + let wpos = range.min + Vec3::new(x, y, z); + volume + .get(wpos) + .ok() + .and_then(|b| b.get_glow()) + .map(|glow| glow_blocks.push((wpos, glow))); + } + } + } + + // Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0) + let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty()); + let glow = calc_light(false, 0, range, self, glow_blocks.into_iter()); + + let mut opaque_limits = None::; + let mut fluid_limits = None::; + let mut air_limits = None::; + let flat_get = { + span!(_guard, "copy to flat array"); + let (w, h, d) = range.size().into_tuple(); + // z can range from -1..range.size().d + 1 + let d = d + 2; + let flat = { + let mut volume = self.cached(); + + const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty); + + // TODO: Once we can manage it sensibly, consider using something like + // Option instead of just assuming air. + let mut flat = vec![AIR; (w * h * d) as usize]; + let mut i = 0; + for x in 0..range.size().w { + for y in 0..range.size().h { + for z in -1..range.size().d + 1 { + let wpos = range.min + Vec3::new(x, y, z); + let block = volume + .get(wpos) + .map(|b| *b) + // TODO: Replace with None or some other more reasonable value, + // since it's not clear this will work properly with liquid. + .unwrap_or(AIR); + if block.is_opaque() { + opaque_limits = opaque_limits + .map(|l| l.including(z)) + .or_else(|| Some(Limits::from_value(z))); + } else if block.is_liquid() { + fluid_limits = fluid_limits + .map(|l| l.including(z)) + .or_else(|| Some(Limits::from_value(z))); + } else { + // Assume air + air_limits = air_limits + .map(|l| l.including(z)) + .or_else(|| Some(Limits::from_value(z))); + }; + flat[i] = block; + i += 1; + } + } + } + flat + }; + + move |Vec3 { x, y, z }| { + // z can range from -1..range.size().d + 1 + let z = z + 1; + match flat.get((x * h * d + y * d + z) as usize).copied() { + Some(b) => b, + None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), } } - flat }; - move |Vec3 { x, y, z }| { - // z can range from -1..range.size().d + 1 - let z = z + 1; - match flat.get((x * h * d + y * d + z) as usize).copied() { - Some(b) => b, - None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h), + // Constrain iterated area + let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) { + (Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque), + (Some(air), Some(fluid), None) => air.intersection(fluid), + (Some(air), None, Some(opaque)) => air.intersection(opaque), + (None, Some(fluid), Some(opaque)) => fluid.intersection(opaque), + // No interfaces (Note: if there are multiple fluid types this could change) + (Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None, + (None, None, None) => { + error!("Impossible unless given an input AABB that has a height of zero"); + None + }, + } + .map_or((0, 0), |limits| { + let (start, end) = limits.into_tuple(); + let start = start.max(0); + let end = end.min(range.size().d - 1).max(start); + (start, end) + }); + + let max_size = + guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y)); + assert!(z_end >= z_start); + let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1); + // NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5 + // + 14). FIXME: Make this function fallible, since the terrain + // information might be dynamically generated which would make this hard + // to enforce. + assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a f32. + let max_bounds: Vec3 = greedy_size.as_::(); + // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, + // which always fits into a usize. + let greedy_size = greedy_size.as_::(); + let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z); + let draw_delta = Vec3::new(1, 1, z_start); + + let get_light = |_: &mut (), pos: Vec3| { + if flat_get(pos).is_opaque() { + 0.0 + } else { + light(pos + range.min) } - } - }; + }; + let get_glow = |_: &mut (), pos: Vec3| glow(pos + range.min); + let get_color = + |_: &mut (), pos: Vec3| flat_get(pos).get_color().unwrap_or(Rgb::zero()); + let get_opacity = |_: &mut (), pos: Vec3| !flat_get(pos).is_opaque(); + let flat_get = |pos| flat_get(pos); + let should_draw = |_: &mut (), pos: Vec3, delta: Vec3, _uv| { + should_draw_greedy(pos, delta, flat_get) + }; + // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. + let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32); + let create_opaque = |atlas_pos, pos, norm, meta| { + TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta) + }; + let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm); - // Constrain iterated area - let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) { - (Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque), - (Some(air), Some(fluid), None) => air.intersection(fluid), - (Some(air), None, Some(opaque)) => air.intersection(opaque), - (None, Some(fluid), Some(opaque)) => fluid.intersection(opaque), - // No interfaces (Note: if there are multiple fluid types this could change) - (Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None, - (None, None, None) => { - error!("Impossible unless given an input AABB that has a height of zero"); - None - }, - } - .map_or((0, 0), |limits| { - let (start, end) = limits.into_tuple(); - let start = start.max(0); - let end = end.min(range.size().d - 1).max(start); - (start, end) - }); - - let max_size = - guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y)); - assert!(z_end >= z_start); - let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1); - // NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5 - // + 14). FIXME: Make this function fallible, since the terrain - // information might be dynamically generated which would make this hard - // to enforce. - assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384); - // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, - // which always fits into a f32. - let max_bounds: Vec3 = greedy_size.as_::(); - // NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16, - // which always fits into a usize. - let greedy_size = greedy_size.as_::(); - let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z); - let draw_delta = Vec3::new(1, 1, z_start); - - let get_light = |_: &mut (), pos: Vec3| { - if flat_get(pos).is_opaque() { - 0.0 - } else { - light(pos + range.min) - } - }; - let get_glow = |_: &mut (), pos: Vec3| glow(pos + range.min); - let get_color = |_: &mut (), pos: Vec3| flat_get(pos).get_color().unwrap_or(Rgb::zero()); - let get_opacity = |_: &mut (), pos: Vec3| !flat_get(pos).is_opaque(); - let flat_get = |pos| flat_get(pos); - let should_draw = |_: &mut (), pos: Vec3, delta: Vec3, _uv| { - should_draw_greedy(pos, delta, flat_get) - }; - // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. - let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32); - let create_opaque = - |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta); - let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm); - - let mut greedy = GreedyMesh::new(max_size); - let mut opaque_mesh = Mesh::new(); - let mut fluid_mesh = Mesh::new(); - greedy.push(GreedyConfig { - data: (), - draw_delta, - greedy_size, - greedy_size_cross, - get_light, - get_glow, - get_opacity, - should_draw, - push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta { - FaceKind::Opaque(meta) => { - opaque_mesh.push_quad(greedy::create_quad( - atlas_origin, - dim, - origin, - draw_dim, - norm, - meta, - |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), - )); + let mut greedy = GreedyMesh::new(max_size); + let mut opaque_mesh = Mesh::new(); + let mut fluid_mesh = Mesh::new(); + greedy.push(GreedyConfig { + data: (), + draw_delta, + greedy_size, + greedy_size_cross, + get_light, + get_glow, + get_opacity, + should_draw, + push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta { + FaceKind::Opaque(meta) => { + opaque_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + meta, + |atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta), + )); + }, + FaceKind::Fluid => { + fluid_mesh.push_quad(greedy::create_quad( + atlas_origin, + dim, + origin, + draw_dim, + norm, + &(), + |atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm), + )); + }, }, - FaceKind::Fluid => { - fluid_mesh.push_quad(greedy::create_quad( - atlas_origin, - dim, - origin, - draw_dim, - norm, - &(), - |atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm), - )); + make_face_texel: |data: &mut (), pos, light, glow| { + TerrainVertex::make_col_light(light, glow, get_color(data, pos)) }, - }, - make_face_texel: |data: &mut (), pos, light, glow| { - TerrainVertex::make_col_light(light, glow, get_color(data, pos)) - }, - }); + }); - let min_bounds = mesh_delta; - let bounds = Aabb { - min: min_bounds, - max: max_bounds + min_bounds, - }; - let (col_lights, col_lights_size) = greedy.finalize(); + let min_bounds = mesh_delta; + let bounds = Aabb { + min: min_bounds, + max: max_bounds + min_bounds, + }; + let (col_lights, col_lights_size) = greedy.finalize(); - ( - opaque_mesh, - fluid_mesh, - Mesh::new(), ( - bounds, - (col_lights, col_lights_size), - Arc::new(light), - Arc::new(glow), - ), - ) + opaque_mesh, + fluid_mesh, + Mesh::new(), + ( + bounds, + (col_lights, col_lights_size), + Arc::new(light), + Arc::new(glow), + ), + ) + } } /// NOTE: Make sure to reflect any changes to how meshing is performanced in diff --git a/voxygen/src/render/bound.rs b/voxygen/src/render/bound.rs deleted file mode 100644 index e601c0adb1..0000000000 --- a/voxygen/src/render/bound.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub struct Bound { - pub(super) bind_group: wgpu::BindGroup, - pub(super) with: T, -} - -impl std::ops::Deref for Bound { - type Target = T; - - fn deref(&self) -> &Self::Target { &self.with } -} - -impl std::ops::DerefMut for Bound { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.with } -} diff --git a/voxygen/src/render/buffer.rs b/voxygen/src/render/buffer.rs deleted file mode 100644 index f4d80a51cc..0000000000 --- a/voxygen/src/render/buffer.rs +++ /dev/null @@ -1,63 +0,0 @@ -use bytemuck::Pod; -use wgpu::util::DeviceExt; - -pub struct Buffer { - pub(super) buf: wgpu::Buffer, - // Size in number of elements - // TODO: determine if this is a good name - len: usize, - phantom_data: std::marker::PhantomData, -} - -impl Buffer { - pub fn new(device: &wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self { - let contents = bytemuck::cast_slice(data); - - Self { - buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents, - usage, - }), - len: data.len(), - phantom_data: std::marker::PhantomData, - } - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { self.len } -} - -pub struct DynamicBuffer(Buffer); - -impl DynamicBuffer { - pub fn new(device: &wgpu::Device, len: usize, usage: wgpu::BufferUsage) -> Self { - let buffer = Buffer { - buf: device.create_buffer(&wgpu::BufferDescriptor { - label: None, - mapped_at_creation: false, - size: len as u64 * std::mem::size_of::() as u64, - usage: usage | wgpu::BufferUsage::COPY_DST, - }), - len, - phantom_data: std::marker::PhantomData, - }; - Self(buffer) - } - - pub fn update(&self, queue: &wgpu::Queue, vals: &[T], offset: usize) { - if !vals.is_empty() { - queue.write_buffer( - &self.buf, - offset as u64 * std::mem::size_of::() as u64, - bytemuck::cast_slice(vals), - ) - } - } -} - -impl std::ops::Deref for DynamicBuffer { - type Target = Buffer; - - fn deref(&self) -> &Self::Target { &self.0 } -} diff --git a/voxygen/src/render/consts.rs b/voxygen/src/render/consts.rs index 3259c7d1aa..0eded13428 100644 --- a/voxygen/src/render/consts.rs +++ b/voxygen/src/render/consts.rs @@ -1,26 +1,36 @@ -use super::buffer::DynamicBuffer; -use bytemuck::Pod; +use super::{gfx_backend, RenderError}; +use gfx::{self, traits::FactoryExt}; /// A handle to a series of constants sitting on the GPU. This is used to hold /// information used in the rendering process that does not change throughout a /// single render pass. -pub struct Consts { - buf: DynamicBuffer, +#[derive(Clone)] +pub struct Consts { + pub buf: gfx::handle::Buffer, } -impl Consts { +impl Consts { /// Create a new `Const`. - pub fn new(device: &wgpu::Device, len: usize) -> Self { + pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self { Self { - // TODO: examine if all our consts need to be updateable - buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::UNIFORM), + buf: factory.create_constant_buffer(len), } } /// Update the GPU-side value represented by this constant handle. - pub fn update(&mut self, queue: &wgpu::Queue, vals: &[T], offset: usize) { - self.buf.update(queue, vals, offset) - } - pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf } + pub fn update( + &mut self, + encoder: &mut gfx::Encoder, + vals: &[T], + offset: usize, + ) -> Result<(), RenderError> { + if vals.is_empty() { + Ok(()) + } else { + encoder + .update_buffer(&self.buf, vals, offset) + .map_err(RenderError::UpdateError) + } + } } diff --git a/voxygen/src/render/error.rs b/voxygen/src/render/error.rs index d3736216c4..8c9352857d 100644 --- a/voxygen/src/render/error.rs +++ b/voxygen/src/render/error.rs @@ -1,54 +1,74 @@ /// Used to represent one of many possible errors that may be omitted by the /// rendering subsystem. +#[derive(Debug)] pub enum RenderError { - RequestDeviceError(wgpu::RequestDeviceError), - MappingError(wgpu::BufferAsyncError), - SwapChainError(wgpu::SwapChainError), + PipelineError(gfx::PipelineStateError), + UpdateError(gfx::UpdateError), + TexUpdateError(gfx::UpdateError<[u16; 3]>), + CombinedError(gfx::CombinedError), + BufferCreationError(gfx::buffer::CreationError), + IncludeError(glsl_include::Error), + MappingError(gfx::mapping::Error), + CopyError(gfx::CopyError<[u16; 3], usize>), CustomError(String), - CouldNotFindAdapter, - ErrorInitializingCompiler, - ShaderError(String, shaderc::Error), } -use std::fmt; -impl fmt::Debug for RenderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::RequestDeviceError(err) => { - f.debug_tuple("RequestDeviceError").field(err).finish() +impl From> for RenderError { + fn from(err: gfx::PipelineStateError) -> Self { Self::PipelineError(err) } +} + +impl From> for RenderError { + fn from(err: gfx::PipelineStateError<&str>) -> Self { + match err { + gfx::PipelineStateError::DescriptorInit(err) => { + gfx::PipelineStateError::DescriptorInit(err) }, - Self::MappingError(err) => f.debug_tuple("MappingError").field(err).finish(), - Self::SwapChainError(err) => f - .debug_tuple("SwapChainError") - // Use Display formatting for this error since they have nice descriptions - .field(&format!("{}", err)) - .finish(), - Self::CustomError(err) => f.debug_tuple("CustomError").field(err).finish(), - Self::CouldNotFindAdapter => f.debug_tuple("CouldNotFindAdapter").finish(), - Self::ErrorInitializingCompiler => f.debug_tuple("ErrorInitializingCompiler").finish(), - Self::ShaderError(shader_name, err) => write!( - f, - "\"{}\" shader failed to compile due to the following error: {}", - shader_name, err - ), + err => err, } + .into() } } - -impl From for RenderError { - fn from(err: wgpu::RequestDeviceError) -> Self { Self::RequestDeviceError(err) } -} - -impl From for RenderError { - fn from(err: wgpu::BufferAsyncError) -> Self { Self::MappingError(err) } -} - -impl From for RenderError { - fn from(err: wgpu::SwapChainError) -> Self { Self::SwapChainError(err) } -} - -impl From<(&str, shaderc::Error)> for RenderError { - fn from((shader_name, err): (&str, shaderc::Error)) -> Self { - Self::ShaderError(shader_name.into(), err) +impl From for RenderError { + fn from(err: gfx::shade::ProgramError) -> Self { + gfx::PipelineStateError::::Program(err).into() } } +impl From> for RenderError { + fn from(err: gfx::UpdateError) -> Self { Self::UpdateError(err) } +} + +impl From> for RenderError { + fn from(err: gfx::UpdateError<[u16; 3]>) -> Self { Self::TexUpdateError(err) } +} + +impl From for RenderError { + fn from(err: gfx::CombinedError) -> Self { Self::CombinedError(err) } +} + +impl From for RenderError { + fn from(err: gfx::TargetViewError) -> Self { Self::CombinedError(err.into()) } +} + +impl From for RenderError { + fn from(err: gfx::ResourceViewError) -> Self { Self::CombinedError(err.into()) } +} + +impl From for RenderError { + fn from(err: gfx::texture::CreationError) -> Self { Self::CombinedError(err.into()) } +} + +impl From for RenderError { + fn from(err: gfx::buffer::CreationError) -> Self { Self::BufferCreationError(err) } +} + +impl From for RenderError { + fn from(err: glsl_include::Error) -> Self { Self::IncludeError(err) } +} + +impl From for RenderError { + fn from(err: gfx::mapping::Error) -> Self { Self::MappingError(err) } +} + +impl From> for RenderError { + fn from(err: gfx::CopyError<[u16; 3], usize>) -> Self { Self::CopyError(err) } +} diff --git a/voxygen/src/render/instances.rs b/voxygen/src/render/instances.rs index 0638dddbd6..c53d5ee2c2 100644 --- a/voxygen/src/render/instances.rs +++ b/voxygen/src/render/instances.rs @@ -1,26 +1,34 @@ -use super::buffer::DynamicBuffer; -use bytemuck::Pod; +use super::{gfx_backend, RenderError}; +use gfx::{ + self, + buffer::Role, + memory::{Bind, Usage}, + Factory, +}; /// Represents a mesh that has been sent to the GPU. -pub struct Instances { - buf: DynamicBuffer, +pub struct Instances { + pub ibuf: gfx::handle::Buffer, } -impl Instances { - pub fn new(device: &wgpu::Device, len: usize) -> Self { - Self { - // TODO: examine if we have Instances that are not updated (e.g. sprites) and if there - // would be any gains from separating those out - buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::VERTEX), - } +impl Instances { + pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result { + Ok(Self { + ibuf: factory + .create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty()) + .map_err(RenderError::BufferCreationError)?, + }) } - // TODO: count vs len naming scheme?? - pub fn count(&self) -> usize { self.buf.len() } + pub fn count(&self) -> usize { self.ibuf.len() } - pub fn update(&mut self, queue: &wgpu::Queue, vals: &[T], offset: usize) { - self.buf.update(queue, vals, offset) + pub fn update( + &mut self, + encoder: &mut gfx::Encoder, + instances: &[T], + ) -> Result<(), RenderError> { + encoder + .update_buffer(&self.ibuf, instances, 0) + .map_err(RenderError::UpdateError) } - - pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf } } diff --git a/voxygen/src/render/mesh.rs b/voxygen/src/render/mesh.rs index b0a2401884..a8c6b6445e 100644 --- a/voxygen/src/render/mesh.rs +++ b/voxygen/src/render/mesh.rs @@ -1,12 +1,15 @@ -use super::Vertex; +use super::Pipeline; use core::{iter::FromIterator, ops::Range}; /// A `Vec`-based mesh structure used to store mesh data on the CPU. -pub struct Mesh { - verts: Vec, +pub struct Mesh { + verts: Vec, } -impl Clone for Mesh { +impl Clone for Mesh

+where + P::Vertex: Clone, +{ fn clone(&self) -> Self { Self { verts: self.verts.clone(), @@ -14,7 +17,7 @@ impl Clone for Mesh { } } -impl Mesh { +impl Mesh

{ /// Create a new `Mesh`. #[allow(clippy::new_without_default)] // TODO: Pending review in #587 pub fn new() -> Self { Self { verts: Vec::new() } } @@ -23,103 +26,83 @@ impl Mesh { pub fn clear(&mut self) { self.verts.clear(); } /// Get a slice referencing the vertices of this mesh. - pub fn vertices(&self) -> &[V] { &self.verts } + pub fn vertices(&self) -> &[P::Vertex] { &self.verts } /// Get a mutable slice referencing the vertices of this mesh. - pub fn vertices_mut(&mut self) -> &mut [V] { &mut self.verts } - - /// Get a mutable vec referencing the vertices of this mesh. - pub fn vertices_mut_vec(&mut self) -> &mut Vec { &mut self.verts } + pub fn vertices_mut(&mut self) -> &mut [P::Vertex] { &mut self.verts } /// Push a new vertex onto the end of this mesh. - pub fn push(&mut self, vert: V) { self.verts.push(vert); } + pub fn push(&mut self, vert: P::Vertex) { self.verts.push(vert); } /// Push a new polygon onto the end of this mesh. - pub fn push_tri(&mut self, tri: Tri) { + pub fn push_tri(&mut self, tri: Tri

) { self.verts.push(tri.a); self.verts.push(tri.b); self.verts.push(tri.c); } /// Push a new quad onto the end of this mesh. - pub fn push_quad(&mut self, quad: Quad) { + pub fn push_quad(&mut self, quad: Quad

) { // A quad is composed of two triangles. The code below converts the former to // the latter. - if V::QUADS_INDEX.is_some() { - // 0, 1, 2, 2, 1, 3 - // b, c, a, a, c, d - self.verts.push(quad.b); - self.verts.push(quad.c); - self.verts.push(quad.a); - self.verts.push(quad.d); - } else { - // Tri 1 - self.verts.push(quad.a); - self.verts.push(quad.b); - self.verts.push(quad.c); - // Tri 2 - self.verts.push(quad.c); - self.verts.push(quad.d); - self.verts.push(quad.a); - } + // Tri 1 + self.verts.push(quad.a.clone()); + self.verts.push(quad.b); + self.verts.push(quad.c.clone()); + + // Tri 2 + self.verts.push(quad.c); + self.verts.push(quad.d); + self.verts.push(quad.a); } /// Overwrite a quad - pub fn replace_quad(&mut self, index: usize, quad: Quad) { - if V::QUADS_INDEX.is_some() { - debug_assert!(index % 4 == 0); - assert!(index + 3 < self.verts.len()); - self.verts[index] = quad.b; - self.verts[index + 1] = quad.c; - self.verts[index + 2] = quad.a; - self.verts[index + 3] = quad.d; - } else { - debug_assert!(index % 3 == 0); - assert!(index + 5 < self.verts.len()); - // Tri 1 - self.verts[index] = quad.a; - self.verts[index + 1] = quad.b; - self.verts[index + 2] = quad.c; + pub fn replace_quad(&mut self, index: usize, quad: Quad

) { + debug_assert!(index % 3 == 0); + assert!(index + 5 < self.verts.len()); + // Tri 1 + self.verts[index] = quad.a.clone(); + self.verts[index + 1] = quad.b; + self.verts[index + 2] = quad.c.clone(); - // Tri 2 - self.verts[index + 3] = quad.c; - self.verts[index + 4] = quad.d; - self.verts[index + 5] = quad.a; - } + // Tri 2 + self.verts[index + 3] = quad.c; + self.verts[index + 4] = quad.d; + self.verts[index + 5] = quad.a; } /// Push the vertices of another mesh onto the end of this mesh. - pub fn push_mesh(&mut self, other: &Mesh) { self.verts.extend_from_slice(other.vertices()); } + pub fn push_mesh(&mut self, other: &Mesh

) { self.verts.extend_from_slice(other.vertices()); } /// Map and push the vertices of another mesh onto the end of this mesh. - pub fn push_mesh_map V>(&mut self, other: &Mesh, mut f: F) { + pub fn push_mesh_map P::Vertex>(&mut self, other: &Mesh

, mut f: F) { // Reserve enough space in our Vec. This isn't necessary, but it tends to reduce // the number of required (re)allocations. self.verts.reserve(other.vertices().len()); for vert in other.vertices() { - self.verts.push(f(*vert)); + self.verts.push(f(vert.clone())); } } - pub fn iter(&self) -> std::slice::Iter { self.verts.iter() } + pub fn iter(&self) -> std::slice::Iter { self.verts.iter() } /// NOTE: Panics if vertex_range is out of bounds of vertices. - pub fn iter_mut(&mut self, vertex_range: Range) -> std::slice::IterMut { + pub fn iter_mut(&mut self, vertex_range: Range) -> std::slice::IterMut { self.verts[vertex_range].iter_mut() } } -impl IntoIterator for Mesh { - type IntoIter = std::vec::IntoIter; - type Item = V; +impl IntoIterator for Mesh

{ + type IntoIter = std::vec::IntoIter; + type Item = P::Vertex; fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() } } -impl FromIterator> for Mesh { - fn from_iter>>(tris: I) -> Self { +impl FromIterator> for Mesh

{ + fn from_iter>>(tris: I) -> Self { tris.into_iter().fold(Self::new(), |mut this, tri| { this.push_tri(tri); this @@ -127,8 +110,8 @@ impl FromIterator> for Mesh { } } -impl FromIterator> for Mesh { - fn from_iter>>(quads: I) -> Self { +impl FromIterator> for Mesh

{ + fn from_iter>>(quads: I) -> Self { quads.into_iter().fold(Self::new(), |mut this, quad| { this.push_quad(quad); this @@ -137,35 +120,40 @@ impl FromIterator> for Mesh { } /// Represents a triangle stored on the CPU. -pub struct Tri { - a: V, - b: V, - c: V, +pub struct Tri { + a: P::Vertex, + b: P::Vertex, + c: P::Vertex, } -impl Tri { - pub fn new(a: V, b: V, c: V) -> Self { Self { a, b, c } } +impl Tri

{ + pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex) -> Self { Self { a, b, c } } } /// Represents a quad stored on the CPU. -pub struct Quad { - a: V, - b: V, - c: V, - d: V, +pub struct Quad { + a: P::Vertex, + b: P::Vertex, + c: P::Vertex, + d: P::Vertex, } -impl Quad { - pub fn new(a: V, b: V, c: V, d: V) -> Self { Self { a, b, c, d } } +impl Quad

{ + pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self { + Self { a, b, c, d } + } - pub fn rotated_by(self, n: usize) -> Self { + pub fn rotated_by(self, n: usize) -> Self + where + P::Vertex: Clone, + { let verts = [self.a, self.b, self.c, self.d]; Self { - a: verts[n % 4], - b: verts[(1 + n) % 4], - c: verts[(2 + n) % 4], - d: verts[(3 + n) % 4], + a: verts[n % 4].clone(), + b: verts[(1 + n) % 4].clone(), + c: verts[(2 + n) % 4].clone(), + d: verts[(3 + n) % 4].clone(), } } } diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index c4af1c02ad..2c96ec0020 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -1,5 +1,3 @@ -pub mod bound; -mod buffer; #[allow(clippy::single_component_path_imports)] // TODO: Pending review in #587 pub mod consts; mod error; @@ -12,55 +10,58 @@ pub mod texture; // Reexports pub use self::{ - bound::Bound, - buffer::Buffer, consts::Consts, error::RenderError, instances::Instances, mesh::{Mesh, Quad, Tri}, - model::{DynamicModel, Model, SubModel}, + model::{DynamicModel, Model}, pipelines::{ - clouds::Locals as CloudsLocals, - debug::{DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex}, + clouds::{create_mesh as create_clouds_mesh, CloudsPipeline, Locals as CloudsLocals}, figure::{ - BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel, + BoneData as FigureBoneData, BoneMeshes, FigureModel, FigurePipeline, Locals as FigureLocals, }, - fluid::Vertex as FluidVertex, - lod_terrain::{LodData, Vertex as LodTerrainVertex}, - particle::{Instance as ParticleInstance, Vertex as ParticleVertex}, - postprocess::Locals as PostProcessLocals, - shadow::{Locals as ShadowLocals, PointLightMatrix}, - skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex}, - sprite::{ - Instance as SpriteInstance, SpriteGlobalsBindGroup, SpriteVerts, - Vertex as SpriteVertex, VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE, + fluid::FluidPipeline, + lod_terrain::{Locals as LodTerrainLocals, LodData, LodTerrainPipeline}, + particle::{Instance as ParticleInstance, ParticlePipeline}, + postprocess::{ + create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, }, - terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex}, + shadow::{Locals as ShadowLocals, ShadowPipeline}, + skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline}, + sprite::{Instance as SpriteInstance, Locals as SpriteLocals, SpritePipeline}, + terrain::{Locals as TerrainLocals, TerrainPipeline}, ui::{ create_quad as create_ui_quad, create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri, - BoundLocals as UiBoundLocals, Locals as UiLocals, Mode as UiMode, - TextureBindGroup as UiTextureBindGroup, Vertex as UiVertex, + Locals as UiLocals, Mode as UiMode, UiPipeline, }, - GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light, Shadow, + GlobalModel, Globals, Light, Shadow, }, renderer::{ - drawer::{ - DebugDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer, - PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer, - TerrainShadowDrawer, ThirdPassDrawer, UiDrawer, - }, - ColLightInfo, Renderer, + ColLightFmt, ColLightInfo, LodAltFmt, LodColorFmt, LodTextureFmt, Renderer, + ShadowDepthStencilFmt, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, }, texture::Texture, }; -pub use wgpu::{AddressMode, FilterMode}; +pub use gfx::texture::{FilterMethod, WrapMode}; -pub trait Vertex: Clone + bytemuck::Pod { - const STRIDE: wgpu::BufferAddress; - // Whether these types of verts use the quad index buffer for drawing them - const QUADS_INDEX: Option; +#[cfg(feature = "gl")] +use gfx_device_gl as gfx_backend; + +/// Used to represent a specific rendering configuration. +/// +/// Note that pipelines are tied to the +/// rendering backend, and as such it is necessary to modify the rendering +/// subsystem when adding new pipelines - custom pipelines are not currently an +/// objective of the rendering subsystem. +/// +/// # Examples +/// +/// - `SkyboxPipeline` +/// - `FigurePipeline` +pub trait Pipeline { + type Vertex: Clone + gfx::traits::Pod + gfx::pso::buffer::Structure; } use serde::{Deserialize, Serialize}; @@ -242,30 +243,6 @@ impl Default for UpscaleMode { fn default() -> Self { Self { factor: 1.0 } } } -/// Present modes -/// See https://docs.rs/wgpu/0.7.0/wgpu/enum.PresentMode.html -#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] -pub enum PresentMode { - Fifo, - Mailbox, - #[serde(other)] - Immediate, -} - -impl Default for PresentMode { - fn default() -> Self { Self::Immediate } -} - -impl From for wgpu::PresentMode { - fn from(mode: PresentMode) -> Self { - match mode { - PresentMode::Fifo => wgpu::PresentMode::Fifo, - PresentMode::Mailbox => wgpu::PresentMode::Mailbox, - PresentMode::Immediate => wgpu::PresentMode::Immediate, - } - } -} - /// Render modes #[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] #[serde(default)] @@ -276,6 +253,4 @@ pub struct RenderMode { pub lighting: LightingMode, pub shadow: ShadowMode, pub upscale_mode: UpscaleMode, - pub present_mode: PresentMode, - pub profiler_enabled: bool, } diff --git a/voxygen/src/render/model.rs b/voxygen/src/render/model.rs index 7ba2dbb401..119f314feb 100644 --- a/voxygen/src/render/model.rs +++ b/voxygen/src/render/model.rs @@ -1,89 +1,69 @@ -use super::{ - buffer::{Buffer, DynamicBuffer}, - mesh::Mesh, - Vertex, +use super::{gfx_backend, mesh::Mesh, Pipeline, RenderError}; +use gfx::{ + buffer::Role, + memory::{Bind, Usage}, + traits::FactoryExt, + Factory, }; use std::ops::Range; /// Represents a mesh that has been sent to the GPU. -pub struct SubModel<'a, V: Vertex> { +pub struct Model { + pub vbuf: gfx::handle::Buffer, pub vertex_range: Range, - buf: &'a wgpu::Buffer, - phantom_data: std::marker::PhantomData, } -impl<'a, V: Vertex> SubModel<'a, V> { - pub(super) fn buf(&self) -> wgpu::BufferSlice<'a> { - let start = self.vertex_range.start as wgpu::BufferAddress * V::STRIDE; - let end = self.vertex_range.end as wgpu::BufferAddress * V::STRIDE; - self.buf.slice(start..end) +impl Model

{ + pub fn new(factory: &mut gfx_backend::Factory, mesh: &Mesh

) -> Self { + Self { + vbuf: factory.create_vertex_buffer(mesh.vertices()), + vertex_range: 0..mesh.vertices().len() as u32, + } } - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> u32 { self.vertex_range.end - self.vertex_range.start } -} + pub fn vertex_range(&self) -> Range { self.vertex_range.clone() } -/// Represents a mesh that has been sent to the GPU. -pub struct Model { - vbuf: Buffer, -} - -impl Model { - /// Returns None if the provided mesh is empty - pub fn new(device: &wgpu::Device, mesh: &Mesh) -> Option { - if mesh.vertices().is_empty() { - return None; + /// Create a model with a slice of a portion of this model to send to the + /// renderer. + pub fn submodel(&self, vertex_range: Range) -> Model

{ + Model { + vbuf: self.vbuf.clone(), + vertex_range, } + } +} - Some(Self { - vbuf: Buffer::new(device, wgpu::BufferUsage::VERTEX, mesh.vertices()), +/// Represents a mesh on the GPU which can be updated dynamically. +pub struct DynamicModel { + pub vbuf: gfx::handle::Buffer, +} + +impl DynamicModel

{ + pub fn new(factory: &mut gfx_backend::Factory, size: usize) -> Result { + Ok(Self { + vbuf: factory + .create_buffer(size, Role::Vertex, Usage::Dynamic, Bind::empty()) + .map_err(RenderError::BufferCreationError)?, }) } /// Create a model with a slice of a portion of this model to send to the /// renderer. - pub fn submodel(&self, vertex_range: Range) -> SubModel { - SubModel { + pub fn submodel(&self, vertex_range: Range) -> Model

{ + Model { + vbuf: self.vbuf.clone(), vertex_range, - buf: self.buf(), - phantom_data: std::marker::PhantomData, } } - pub(super) fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { self.vbuf.len() } -} - -/// Represents a mesh that has been sent to the GPU. -pub struct DynamicModel { - vbuf: DynamicBuffer, -} - -impl DynamicModel { - pub fn new(device: &wgpu::Device, size: usize) -> Self { - Self { - vbuf: DynamicBuffer::new(device, size, wgpu::BufferUsage::VERTEX), - } + pub fn update( + &self, + encoder: &mut gfx::Encoder, + mesh: &Mesh

, + offset: usize, + ) -> Result<(), RenderError> { + encoder + .update_buffer(&self.vbuf, mesh.vertices(), offset) + .map_err(RenderError::UpdateError) } - - pub fn update(&self, queue: &wgpu::Queue, mesh: &Mesh, offset: usize) { - self.vbuf.update(queue, mesh.vertices(), offset) - } - - /// Create a model with a slice of a portion of this model to send to the - /// renderer. - pub fn submodel(&self, vertex_range: Range) -> SubModel { - SubModel { - vertex_range, - buf: self.buf(), - phantom_data: std::marker::PhantomData, - } - } - - pub fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { self.vbuf.len() } } diff --git a/voxygen/src/render/pipelines/blit.rs b/voxygen/src/render/pipelines/blit.rs deleted file mode 100644 index fc7050f9c7..0000000000 --- a/voxygen/src/render/pipelines/blit.rs +++ /dev/null @@ -1,123 +0,0 @@ -pub struct BindGroup { - pub(in super::super) bind_group: wgpu::BindGroup, -} - -pub struct BlitLayout { - pub layout: wgpu::BindGroupLayout, -} - -impl BlitLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // Color source - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind( - &self, - device: &wgpu::Device, - src_color: &wgpu::TextureView, - sampler: &wgpu::Sampler, - ) -> BindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(src_color), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - ], - }); - - BindGroup { bind_group } - } -} - -pub struct BlitPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl BlitPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - sc_desc: &wgpu::SwapChainDescriptor, - layout: &BlitLayout, - ) -> Self { - common_base::span!(_guard, "BlitPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Blit pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&layout.layout], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Blit pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: sc_desc.format, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} diff --git a/voxygen/src/render/pipelines/clouds.rs b/voxygen/src/render/pipelines/clouds.rs index 7796f47689..7ad0c782b9 100644 --- a/voxygen/src/render/pipelines/clouds.rs +++ b/voxygen/src/render/pipelines/clouds.rs @@ -1,15 +1,40 @@ use super::{ - super::{AaMode, Consts}, - GlobalsLayouts, + super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri}, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, }; -use bytemuck::{Pod, Zeroable}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - proj_mat_inv: [[f32; 4]; 4], - view_mat_inv: [[f32; 4]; 4], +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + proj_mat_inv: [[f32; 4]; 4] = "proj_mat_inv", + view_mat_inv: [[f32; 4]; 4] = "view_mat_inv", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + map: gfx::TextureSampler<[f32; 4]> = "t_map", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + color_sampler: gfx::TextureSampler<::View> = "src_color", + depth_sampler: gfx::TextureSampler<::View> = "src_depth", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + } } impl Default for Locals { @@ -25,180 +50,28 @@ impl Locals { } } -pub struct BindGroup { - pub(in super::super) bind_group: wgpu::BindGroup, +pub struct CloudsPipeline; + +impl Pipeline for CloudsPipeline { + type Vertex = Vertex; } -pub struct CloudsLayout { - pub layout: wgpu::BindGroupLayout, -} - -impl CloudsLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // Color source - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Depth source - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Locals - wgpu::BindGroupLayoutEntry { - binding: 4, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind( - &self, - device: &wgpu::Device, - src_color: &wgpu::TextureView, - src_depth: &wgpu::TextureView, - sampler: &wgpu::Sampler, - depth_sampler: &wgpu::Sampler, - locals: &Consts, - ) -> BindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(src_color), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(src_depth), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::Sampler(depth_sampler), - }, - wgpu::BindGroupEntry { - binding: 4, - resource: locals.buf().as_entire_binding(), - }, - ], - }); - - BindGroup { bind_group } - } -} - -pub struct CloudsPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl CloudsPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - layout: &CloudsLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "CloudsPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Clouds pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &layout.layout], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Clouds pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } +pub fn create_mesh() -> Mesh { + let mut mesh = Mesh::new(); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [ 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0] }, + Vertex { pos: [-1.0, -1.0] }, + )); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [1.0, -1.0] }, + Vertex { pos: [1.0, 1.0] }, + Vertex { pos: [-1.0, 1.0] }, + )); + + mesh } diff --git a/voxygen/src/render/pipelines/debug.rs b/voxygen/src/render/pipelines/debug.rs deleted file mode 100644 index e096510dc4..0000000000 --- a/voxygen/src/render/pipelines/debug.rs +++ /dev/null @@ -1,172 +0,0 @@ -use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; -use vek::*; - -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pub pos: [f32; 3], -} - -impl Vertex { - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }], - } - } -} - -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = None; - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - /// pos is [f32; 4] instead of [f32; 3] so that Locals's size is a multiple - /// of 8 bytes (which is required by gfx), the last component is ignored - /// by the shader - pub pos: [f32; 4], - pub color: [f32; 4], -} - -pub type BoundLocals = Bound>; - -impl From> for Vertex { - fn from(pos: Vec3) -> Vertex { - Vertex { - pos: [pos.x, pos.y, pos.z], - } - } -} - -pub struct DebugPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl DebugPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layouts: &GlobalsLayouts, - layout: &DebugLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "DebugPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Debug pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layouts.globals, &layout.locals], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Debug pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} - -pub struct DebugLayout { - pub locals: wgpu::BindGroupLayout, -} - -impl DebugLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }), - } - } - - pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts) -> BoundLocals { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.locals, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: locals.buf().as_entire_binding(), - }], - }); - - BoundLocals { - bind_group, - with: locals, - } - } -} diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index 662f5db4c6..d9f5f45b74 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -1,31 +1,57 @@ use super::{ - super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model}, - terrain::Vertex, + super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, }; use crate::mesh::greedy::GreedyMesh; -use bytemuck::{Pod, Zeroable}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, state::ColorMask, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - model_mat: [[f32; 4]; 4], - highlight_col: [f32; 4], - model_light: [f32; 4], - model_glow: [f32; 4], - atlas_offs: [i32; 4], - model_pos: [f32; 3], - flags: u32, -} +gfx_defines! { + constant Locals { + model_mat: [[f32; 4]; 4] = "model_mat", + highlight_col: [f32; 4] = "highlight_col", + model_light: [f32; 4] = "model_light", + model_glow: [f32; 4] = "model_glow", + atlas_offs: [i32; 4] = "atlas_offs", + model_pos: [f32; 3] = "model_pos", + flags: u32 = "flags", + } -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct BoneData { - bone_mat: [[f32; 4]; 4], - normals_mat: [[f32; 4]; 4], -} + constant BoneData { + bone_mat: [[f32; 4]; 4] = "bone_mat", + normals_mat: [[f32; 4]; 4] = "normals_mat", + } -pub type BoundLocals = Bound<(Consts, Consts)>; + pipeline pipe { + vbuf: gfx::VertexBuffer<::Vertex> = (), + // abuf: gfx::VertexBuffer<::Vertex> = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + bones: gfx::ConstantBuffer = "u_bones", + lights: gfx::ConstantBuffer = "u_lights", + shadows: gfx::ConstantBuffer = "u_shadows", + + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))), + } +} impl Locals { pub fn new( @@ -79,8 +105,14 @@ impl Default for BoneData { fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) } } +pub struct FigurePipeline; + +impl Pipeline for FigurePipeline { + type Vertex = ::Vertex; +} + pub struct FigureModel { - pub opaque: Model, + pub opaque: Model, /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different * LOD levels. */ } @@ -97,157 +129,4 @@ impl FigureModel { } } -pub type BoneMeshes = (Mesh, anim::vek::Aabb); - -pub struct FigureLayout { - pub locals: wgpu::BindGroupLayout, -} - -impl FigureLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // locals - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - // bone data - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind_locals( - &self, - device: &wgpu::Device, - locals: Consts, - bone_data: Consts, - ) -> BoundLocals { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.locals, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: locals.buf().as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: bone_data.buf().as_entire_binding(), - }, - ], - }); - - BoundLocals { - bind_group, - with: (locals, bone_data), - } - } -} - -pub struct FigurePipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl FigurePipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - layout: &FigureLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "FigurePipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Figure pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[ - &global_layout.globals, - &global_layout.shadow_textures, - &layout.locals, - &global_layout.col_light, - ], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Figure pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} +pub type BoneMeshes = (Mesh, anim::vek::Aabb); diff --git a/voxygen/src/render/pipelines/fluid.rs b/voxygen/src/render/pipelines/fluid.rs index 3d49fb95c3..a871aa3c4a 100644 --- a/voxygen/src/render/pipelines/fluid.rs +++ b/voxygen/src/render/pipelines/fluid.rs @@ -1,12 +1,42 @@ -use super::super::{AaMode, GlobalsLayouts, TerrainLayout, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::{ + super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, +}; +use gfx::{ + self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, + gfx_vertex_struct_meta, state::ColorMask, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pos_norm: u32, +gfx_defines! { + vertex Vertex { + pos_norm: u32 = "v_pos_norm", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + lights: gfx::ConstantBuffer = "u_lights", + shadows: gfx::ConstantBuffer = "u_shadows", + + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + waves: gfx::TextureSampler<[f32; 4]> = "t_waves", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } } impl Vertex { @@ -31,116 +61,10 @@ impl Vertex { | (norm_bits & 0x7) << 29, } } - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Uint32]; - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &ATTRIBUTES, - } - } } -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = Some(wgpu::IndexFormat::Uint16); - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; -} - -pub struct FluidPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl FluidPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - terrain_layout: &TerrainLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "FluidPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Fluid pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[ - &global_layout.globals, - &global_layout.shadow_textures, - &terrain_layout.locals, - ], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Fluid pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } +pub struct FluidPipeline; + +impl Pipeline for FluidPipeline { + type Vertex = Vertex; } diff --git a/voxygen/src/render/pipelines/lod_terrain.rs b/voxygen/src/render/pipelines/lod_terrain.rs index 4257428dbd..9003525dc5 100644 --- a/voxygen/src/render/pipelines/lod_terrain.rs +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -1,12 +1,40 @@ -use super::super::{AaMode, GlobalsLayouts, Renderer, Texture, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::{ + super::{ + LodAltFmt, LodColorFmt, LodTextureFmt, Pipeline, Renderer, Texture, TgtColorFmt, + TgtDepthStencilFmt, + }, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, texture::SamplerInfo, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pos: [f32; 2], +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + nul: [f32; 4] = "nul", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + map: gfx::TextureSampler<[f32; 4]> = "t_map", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } } impl Vertex { @@ -15,210 +43,75 @@ impl Vertex { pos: pos.into_array(), } } - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Float32x2]; - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &ATTRIBUTES, - } - } } -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = Some(wgpu::IndexFormat::Uint32); - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; +impl Locals { + pub fn default() -> Self { Self { nul: [0.0; 4] } } +} + +pub struct LodTerrainPipeline; + +impl Pipeline for LodTerrainPipeline { + type Vertex = Vertex; } pub struct LodData { - pub map: Texture, - pub alt: Texture, - pub horizon: Texture, + pub map: Texture, + pub alt: Texture, + pub horizon: Texture, pub tgt_detail: u32, } impl LodData { - pub fn dummy(renderer: &mut Renderer) -> Self { - let map_size = Vec2::new(1, 1); - //let map_border = [0.0, 0.0, 0.0, 0.0]; - let map_image = [0]; - let alt_image = [0]; - let horizon_image = [0x_00_01_00_01]; - - Self::new( - renderer, - map_size, - &map_image, - &alt_image, - &horizon_image, - 1, - //map_border.into(), - ) - } - pub fn new( renderer: &mut Renderer, - map_size: Vec2, + map_size: Vec2, lod_base: &[u32], lod_alt: &[u32], lod_horizon: &[u32], tgt_detail: u32, - //border_color: gfx::texture::PackedColor, + border_color: gfx::texture::PackedColor, ) -> Self { - let mut create_texture = |format, data, filter| { - let texture_info = wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: map_size.x, - height: map_size.y, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, - }; - - let sampler_info = wgpu::SamplerDescriptor { - label: None, - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: filter, - min_filter: filter, - mipmap_filter: wgpu::FilterMode::Nearest, - border_color: Some(wgpu::SamplerBorderColor::TransparentBlack), - ..Default::default() - }; - - let view_info = wgpu::TextureViewDescriptor { - label: None, - format: Some(format), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - renderer.create_texture_with_data_raw( - &texture_info, - &view_info, - &sampler_info, - bytemuck::cast_slice(data), - ) - }; - let map = create_texture( - wgpu::TextureFormat::Rgba8UnormSrgb, - lod_base, - wgpu::FilterMode::Linear, + let kind = gfx::texture::Kind::D2(map_size.x, map_size.y, gfx::texture::AaMode::Single); + let info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Border, ); - // SamplerInfo { - // border: border_color, - let alt = create_texture( - wgpu::TextureFormat::Rgba8Unorm, - lod_alt, - wgpu::FilterMode::Linear, - ); - // SamplerInfo { - // border: [0.0, 0.0, 0.0, 0.0].into(), - let horizon = create_texture( - wgpu::TextureFormat::Rgba8Unorm, - lod_horizon, - wgpu::FilterMode::Linear, - ); - // SamplerInfo { - // border: [1.0, 0.0, 1.0, 0.0].into(), - Self { - map, - alt, - horizon, + map: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_base)], + SamplerInfo { + border: border_color, + ..info + }, + ) + .expect("Failed to generate map texture"), + alt: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_alt)], + SamplerInfo { + border: [0.0, 0.0, 0.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate alt texture"), + horizon: renderer + .create_texture_immutable_raw( + kind, + gfx::texture::Mipmap::Provided, + &[gfx::memory::cast_slice(lod_horizon)], + SamplerInfo { + border: [1.0, 0.0, 1.0, 0.0].into(), + ..info + }, + ) + .expect("Failed to generate horizon texture"), tgt_detail, } } } - -pub struct LodTerrainPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl LodTerrainPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - aa_mode: AaMode, - ) -> Self { - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Lod terrain pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Lod terrain pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 846a4d7339..ac23906e00 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,6 +1,4 @@ -pub mod blit; pub mod clouds; -pub mod debug; pub mod figure; pub mod fluid; pub mod lod_terrain; @@ -12,70 +10,56 @@ pub mod sprite; pub mod terrain; pub mod ui; -use super::{Consts, Texture}; +use super::Consts; use crate::scene::camera::CameraMode; -use bytemuck::{Pod, Zeroable}; use common::terrain::BlockKind; +use gfx::{self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta}; use vek::*; -// TODO: auto insert these into shaders -pub const MAX_POINT_LIGHT_COUNT: usize = 20; +pub const MAX_POINT_LIGHT_COUNT: usize = 31; pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Globals { - /// Transformation from world coordinate space (with focus_off as the - /// origin) to the camera space - view_mat: [[f32; 4]; 4], - proj_mat: [[f32; 4]; 4], - /// proj_mat * view_mat - all_mat: [[f32; 4]; 4], - /// Offset of the camera from the focus position - cam_pos: [f32; 4], - /// Integer portion of the focus position in world coordinates - focus_off: [f32; 4], - /// Fractions portion of the focus position - focus_pos: [f32; 4], - /// NOTE: view_distance.x is the horizontal view distance, view_distance.y - /// is the LOD detail, view_distance.z is the - /// minimum height over any land chunk (i.e. the sea level), and - /// view_distance.w is the maximum height over this minimum height. - /// - /// TODO: Fix whatever alignment issue requires these uniforms to be - /// aligned. - view_distance: [f32; 4], - time_of_day: [f32; 4], // TODO: Make this f64. - sun_dir: [f32; 4], - moon_dir: [f32; 4], - tick: [f32; 4], - /// x, y represent the resolution of the screen; - /// w, z represent the near and far planes of the shadow map. - screen_res: [f32; 4], - light_shadow_count: [u32; 4], - shadow_proj_factors: [f32; 4], - medium: [u32; 4], - select_pos: [i32; 4], - gamma_exposure: [f32; 4], - ambiance: f32, - cam_mode: u32, - sprite_render_distance: f32, - /// To keep 16-byte-aligned. - globals_dummy: f32, -} +gfx_defines! { + constant Globals { + view_mat: [[f32; 4]; 4] = "view_mat", + proj_mat: [[f32; 4]; 4] = "proj_mat", + all_mat: [[f32; 4]; 4] = "all_mat", + cam_pos: [f32; 4] = "cam_pos", + focus_off: [f32; 4] = "focus_off", + focus_pos: [f32; 4] = "focus_pos", + /// NOTE: view_distance.x is the horizontal view distance, view_distance.y is the LOD + /// detail, view_distance.z is the + /// minimum height over any land chunk (i.e. the sea level), and view_distance.w is the + /// maximum height over this minimum height. + /// + /// TODO: Fix whatever alignment issue requires these uniforms to be aligned. + view_distance: [f32; 4] = "view_distance", + time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64. + sun_dir: [f32; 4] = "sun_dir", + moon_dir: [f32; 4] = "moon_dir", + tick: [f32; 4] = "tick", + /// x, y represent the resolution of the screen; + /// w, z represent the near and far planes of the shadow map. + screen_res: [f32; 4] = "screen_res", + light_shadow_count: [u32; 4] = "light_shadow_count", + shadow_proj_factors: [f32; 4] = "shadow_proj_factors", + medium: [u32; 4] = "medium", + select_pos: [i32; 4] = "select_pos", + gamma_exposure: [f32; 4] = "gamma_exposure", + ambiance: f32 = "ambiance", + cam_mode: u32 = "cam_mode", + sprite_render_distance: f32 = "sprite_render_distance", + } -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Light { - pub pos: [f32; 4], - pub col: [f32; 4], -} + constant Light { + pos: [f32; 4] = "light_pos", + col: [f32; 4] = "light_col", + } -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Shadow { - pos_radius: [f32; 4], + constant Shadow { + pos_radius: [f32; 4] = "shadow_pos_radius", + } } impl Globals { @@ -124,16 +108,15 @@ impl Globals { shadow_planes.x, shadow_planes.y, ], - // TODO: why do we accept values greater than the max? light_shadow_count: [ - usize::min(light_count, MAX_POINT_LIGHT_COUNT) as u32, - usize::min(shadow_count, MAX_FIGURE_SHADOW_COUNT) as u32, - usize::min(directed_light_count, MAX_DIRECTED_LIGHT_COUNT) as u32, + (light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, + (shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, + (directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32, 0, ], shadow_proj_factors: [ - shadow_planes.y / (shadow_planes.y - shadow_planes.x), - shadow_planes.y * shadow_planes.x / (shadow_planes.y - shadow_planes.x), + (shadow_planes.y + shadow_planes.x) / (shadow_planes.y - shadow_planes.x), + (2.0 * shadow_planes.y * shadow_planes.x) / (shadow_planes.y - shadow_planes.x), 0.0, 0.0, ], @@ -146,7 +129,6 @@ impl Globals { ambiance, cam_mode: cam_mode as u32, sprite_render_distance, - globals_dummy: 0.0, } } @@ -232,388 +214,8 @@ impl Default for Shadow { // Global scene data spread across several arrays. pub struct GlobalModel { - // TODO: enforce that these are the lengths in the shaders?? pub globals: Consts, pub lights: Consts, pub shadows: Consts, - pub shadow_mats: shadow::BoundLocals, - pub point_light_matrices: Box<[shadow::PointLightMatrix; 126]>, -} - -pub struct GlobalsBindGroup { - pub(super) bind_group: wgpu::BindGroup, -} - -pub struct ShadowTexturesBindGroup { - pub(super) bind_group: wgpu::BindGroup, -} - -pub struct GlobalsLayouts { - pub globals: wgpu::BindGroupLayout, - pub col_light: wgpu::BindGroupLayout, - pub shadow_textures: wgpu::BindGroupLayout, -} - -pub struct ColLights { - pub(super) bind_group: wgpu::BindGroup, - pub texture: Texture, - phantom: std::marker::PhantomData, -} - -impl GlobalsLayouts { - pub fn base_globals_layout() -> Vec { - vec![ - // Global uniform - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - // Noise tex - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Light uniform - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - // Shadow uniform - wgpu::BindGroupLayoutEntry { - binding: 4, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - // Alt texture - wgpu::BindGroupLayoutEntry { - binding: 5, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 6, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Horizon texture - wgpu::BindGroupLayoutEntry { - binding: 7, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 8, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // light shadows (ie shadows from a light?) - wgpu::BindGroupLayoutEntry { - binding: 9, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - // TODO: is this relevant? - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - // lod map (t_map) - wgpu::BindGroupLayoutEntry { - binding: 10, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 11, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - ] - } - - pub fn new(device: &wgpu::Device) -> Self { - let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Globals layout"), - entries: &Self::base_globals_layout(), - }); - - let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // col lights - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - ], - }); - - let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // point shadow_maps - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Depth, - view_dimension: wgpu::TextureViewDimension::Cube, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: true, - }, - count: None, - }, - // directed shadow maps - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Depth, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: true, - }, - count: None, - }, - ], - }); - - Self { - globals, - col_light, - shadow_textures, - } - } - - // Note: this allocation serves the purpose of not having to duplicate code - pub fn bind_base_globals<'a>( - global_model: &'a GlobalModel, - lod_data: &'a lod_terrain::LodData, - noise: &'a Texture, - ) -> Vec> { - vec![ - // Global uniform - wgpu::BindGroupEntry { - binding: 0, - resource: global_model.globals.buf().as_entire_binding(), - }, - // Noise tex - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&noise.view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&noise.sampler), - }, - // Light uniform - wgpu::BindGroupEntry { - binding: 3, - resource: global_model.lights.buf().as_entire_binding(), - }, - // Shadow uniform - wgpu::BindGroupEntry { - binding: 4, - resource: global_model.shadows.buf().as_entire_binding(), - }, - // Alt texture - wgpu::BindGroupEntry { - binding: 5, - resource: wgpu::BindingResource::TextureView(&lod_data.alt.view), - }, - wgpu::BindGroupEntry { - binding: 6, - resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler), - }, - // Horizon texture - wgpu::BindGroupEntry { - binding: 7, - resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view), - }, - wgpu::BindGroupEntry { - binding: 8, - resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler), - }, - // light shadows - wgpu::BindGroupEntry { - binding: 9, - resource: global_model.shadow_mats.buf().as_entire_binding(), - }, - // lod map (t_map) - wgpu::BindGroupEntry { - binding: 10, - resource: wgpu::BindingResource::TextureView(&lod_data.map.view), - }, - wgpu::BindGroupEntry { - binding: 11, - resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler), - }, - ] - } - - pub fn bind( - &self, - device: &wgpu::Device, - global_model: &GlobalModel, - lod_data: &lod_terrain::LodData, - noise: &Texture, - ) -> GlobalsBindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.globals, - entries: &Self::bind_base_globals(global_model, lod_data, noise), - }); - - GlobalsBindGroup { bind_group } - } - - pub fn bind_shadow_textures( - &self, - device: &wgpu::Device, - point_shadow_map: &Texture, - directed_shadow_map: &Texture, - ) -> ShadowTexturesBindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.shadow_textures, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&point_shadow_map.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&point_shadow_map.sampler), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(&directed_shadow_map.view), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler), - }, - ], - }); - - ShadowTexturesBindGroup { bind_group } - } - - pub fn bind_col_light( - &self, - device: &wgpu::Device, - col_light: Texture, - ) -> ColLights { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.col_light, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&col_light.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&col_light.sampler), - }, - ], - }); - - ColLights { - texture: col_light, - bind_group, - phantom: std::marker::PhantomData, - } - } + pub shadow_mats: Consts, } diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index e067f906f3..7245e9d2ee 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -1,18 +1,79 @@ -use super::super::{AaMode, GlobalsLayouts, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::{ + super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, +}; +use gfx::{ + self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, + gfx_vertex_struct_meta, state::ColorMask, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pub pos: [f32; 3], - // ____BBBBBBBBGGGGGGGGRRRRRRRR - // col: u32 = "v_col", - // ...AANNN - // A = AO - // N = Normal - norm_ao: u32, +gfx_defines! { + vertex Vertex { + pos: [f32; 3] = "v_pos", + // ____BBBBBBBBGGGGGGGGRRRRRRRR + // col: u32 = "v_col", + // ...AANNN + // A = AO + // N = Normal + norm_ao: u32 = "v_norm_ao", + } + + vertex Instance { + // created_at time, so we can calculate time relativity, needed for relative animation. + // can save 32 bits per instance, for particles that are not relatively animated. + inst_time: f32 = "inst_time", + + // The lifespan in seconds of the particle + inst_lifespan: f32 = "inst_lifespan", + + // a seed value for randomness + // can save 32 bits per instance, for particles that don't need randomness/uniqueness. + inst_entropy: f32 = "inst_entropy", + + // modes should probably be seperate shaders, as a part of scaling and optimisation efforts. + // can save 32 bits per instance, and have cleaner tailor made code. + inst_mode: i32 = "inst_mode", + + // A direction for particles to move in + inst_dir: [f32; 3] = "inst_dir", + + // a triangle is: f32 x 3 x 3 x 1 = 288 bits + // a quad is: f32 x 3 x 3 x 2 = 576 bits + // a cube is: f32 x 3 x 3 x 12 = 3456 bits + // this vec is: f32 x 3 x 1 x 1 = 96 bits (per instance!) + // consider using a throw-away mesh and + // positioning the vertex vertices instead, + // if we have: + // - a triangle mesh, and 3 or more instances. + // - a quad mesh, and 6 or more instances. + // - a cube mesh, and 36 or more instances. + inst_pos: [f32; 3] = "inst_pos", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + ibuf: gfx::InstanceBuffer = (), + + globals: gfx::ConstantBuffer = "u_globals", + lights: gfx::ConstantBuffer = "u_lights", + shadows: gfx::ConstantBuffer = "u_shadows", + + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } } impl Vertex { @@ -31,21 +92,6 @@ impl Vertex { norm_ao: norm_bits, } } - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x3, 1 => Uint32]; - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &ATTRIBUTES, - } - } -} - -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = Some(wgpu::IndexFormat::Uint16); - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; } #[derive(Copy, Clone)] @@ -88,40 +134,6 @@ impl ParticleMode { pub fn into_uint(self) -> u32 { self as u32 } } -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Instance { - // created_at time, so we can calculate time relativity, needed for relative animation. - // can save 32 bits per instance, for particles that are not relatively animated. - inst_time: f32, - - // The lifespan in seconds of the particle - inst_lifespan: f32, - - // a seed value for randomness - // can save 32 bits per instance, for particles that don't need randomness/uniqueness. - inst_entropy: f32, - - // modes should probably be seperate shaders, as a part of scaling and optimisation efforts. - // can save 32 bits per instance, and have cleaner tailor made code. - inst_mode: i32, - - // A direction for particles to move in - inst_dir: [f32; 3], - - // a triangle is: f32 x 3 x 3 x 1 = 288 bits - // a quad is: f32 x 3 x 3 x 2 = 576 bits - // a cube is: f32 x 3 x 3 x 12 = 3456 bits - // this vec is: f32 x 3 x 1 x 1 = 96 bits (per instance!) - // consider using a throw-away mesh and - // positioning the vertex verticies instead, - // if we have: - // - a triangle mesh, and 3 or more instances. - // - a quad mesh, and 6 or more instances. - // - a cube mesh, and 36 or more instances. - inst_pos: [f32; 3], -} - impl Instance { pub fn new( inst_time: f64, @@ -157,111 +169,14 @@ impl Instance { inst_dir: (inst_pos2 - inst_pos).into_array(), } } - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 6] = wgpu::vertex_attr_array![2 => Float32, 3 => Float32, 4 => Float32, 5 => Sint32, 6 => Float32x3, 7 => Float32x3]; - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::InputStepMode::Instance, - attributes: &ATTRIBUTES, - } - } } impl Default for Instance { fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Vec3::zero()) } } -pub struct ParticlePipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl ParticlePipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "ParticlePipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Particle pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Particle pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc(), Instance::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - // TODO: use a constant and/or pass in this format on pipeline construction - format: wgpu::TextureFormat::Rgba16Float, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } +pub struct ParticlePipeline; + +impl Pipeline for ParticlePipeline { + type Vertex = Vertex; } diff --git a/voxygen/src/render/pipelines/postprocess.rs b/voxygen/src/render/pipelines/postprocess.rs index 18c982f50c..cda084ca8d 100644 --- a/voxygen/src/render/pipelines/postprocess.rs +++ b/voxygen/src/render/pipelines/postprocess.rs @@ -1,12 +1,40 @@ -use super::super::{Consts, GlobalsLayouts}; -use bytemuck::{Pod, Zeroable}; +use super::{ + super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri, WinColorFmt}, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - proj_mat_inv: [[f32; 4]; 4], - view_mat_inv: [[f32; 4]; 4], +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + proj_mat_inv: [[f32; 4]; 4] = "proj_mat_inv", + view_mat_inv: [[f32; 4]; 4] = "view_mat_inv", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + map: gfx::TextureSampler<[f32; 4]> = "t_map", + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + color_sampler: gfx::TextureSampler<::View> = "src_color", + depth_sampler: gfx::TextureSampler<::View> = "src_depth", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + } } impl Default for Locals { @@ -22,143 +50,28 @@ impl Locals { } } -pub struct BindGroup { - pub(in super::super) bind_group: wgpu::BindGroup, +pub struct PostProcessPipeline; + +impl Pipeline for PostProcessPipeline { + type Vertex = Vertex; } -pub struct PostProcessLayout { - pub layout: wgpu::BindGroupLayout, -} - -impl PostProcessLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // src color - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Locals - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind( - &self, - device: &wgpu::Device, - src_color: &wgpu::TextureView, - sampler: &wgpu::Sampler, - locals: &Consts, - ) -> BindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(src_color), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: locals.buf().as_entire_binding(), - }, - ], - }); - - BindGroup { bind_group } - } -} - -pub struct PostProcessPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl PostProcessPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - sc_desc: &wgpu::SwapChainDescriptor, - global_layout: &GlobalsLayouts, - layout: &PostProcessLayout, - ) -> Self { - common_base::span!(_guard, "PostProcessPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Post process pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &layout.layout], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Post process pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: sc_desc.format, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } +pub fn create_mesh() -> Mesh { + let mut mesh = Mesh::new(); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [ 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0] }, + Vertex { pos: [-1.0, -1.0] }, + )); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [1.0, -1.0] }, + Vertex { pos: [1.0, 1.0] }, + Vertex { pos: [-1.0, 1.0] }, + )); + + mesh } diff --git a/voxygen/src/render/pipelines/shadow.rs b/voxygen/src/render/pipelines/shadow.rs index e51069c28d..1f1069b4b6 100644 --- a/voxygen/src/render/pipelines/shadow.rs +++ b/voxygen/src/render/pipelines/shadow.rs @@ -1,15 +1,54 @@ -use super::super::{ - AaMode, Bound, ColLightInfo, Consts, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout, - TerrainVertex, Texture, +use super::{ + super::{ + ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt, + TerrainLocals, Texture, + }, + figure, terrain, Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, }; -use bytemuck::{Pod, Zeroable}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - shadow_matrices: [[f32; 4]; 4], - texture_mats: [[f32; 4]; 4], +gfx_defines! { + constant Locals { + shadow_matrices: [[f32; 4]; 4] = "shadowMatrices", + texture_mats: [[f32; 4]; 4] = "texture_mat", + } + + pipeline pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } + + pipeline figure_pipe { + // Terrain vertex stuff + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + bones: gfx::ConstantBuffer = "u_bones", + globals: gfx::ConstantBuffer = "u_globals", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_depth_stencil: gfx::DepthTarget = gfx::state::Depth { + fun: gfx::state::Comparison::Less, + write: true, + }, + } } impl Locals { @@ -23,326 +62,29 @@ impl Locals { pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) } } -pub type BoundLocals = Bound>; - -pub struct ShadowLayout { - pub locals: wgpu::BindGroupLayout, -} - -impl ShadowLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }), - } - } - - pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts) -> BoundLocals { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.locals, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: locals.buf().as_entire_binding(), - }], - }); - - BoundLocals { - bind_group, - with: locals, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct PointLightMatrix([[f32; 4]; 4]); - -impl PointLightMatrix { - pub fn new(shadow_mat: Mat4) -> Self { Self(shadow_mat.into_col_arrays()) } - - pub fn default() -> Self { Self::new(Mat4::identity()) } -} - -pub fn create_col_lights( - renderer: &mut Renderer, - (col_lights, col_lights_size): &ColLightInfo, -) -> Texture { - let texture_info = wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: u32::from(col_lights_size.x), - height: u32::from(col_lights_size.y), - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, - }; - - let sampler_info = wgpu::SamplerDescriptor { - label: None, - 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::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - border_color: Some(wgpu::SamplerBorderColor::TransparentBlack), - ..Default::default() - }; - - let view_info = wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Rgba8Unorm), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - renderer.create_texture_with_data_raw( - &texture_info, - &view_info, - &sampler_info, - bytemuck::cast_slice(&col_lights), - ) -} - -pub struct ShadowFigurePipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl ShadowFigurePipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - figure_layout: &FigureLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "new"); - - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Directed figure shadow pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &figure_layout.locals], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Directed shadow figure pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[TerrainVertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Front), - clamp_depth: true, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth24Plus, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: None, - }); - - Self { - pipeline: render_pipeline, - } - } -} - -pub struct ShadowPipeline { - pub pipeline: wgpu::RenderPipeline, -} +pub struct ShadowPipeline; impl ShadowPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - terrain_layout: &TerrainLayout, - aa_mode: AaMode, - ) -> Self { - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Directed shadow pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &terrain_layout.locals], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Directed shadow pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[TerrainVertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Front), - clamp_depth: true, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth24Plus, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: None, - }); - - Self { - pipeline: render_pipeline, - } + pub fn create_col_lights( + renderer: &mut Renderer, + (col_lights, col_lights_size): &ColLightInfo, + ) -> Result, RenderError> { + renderer.create_texture_immutable_raw( + gfx::texture::Kind::D2( + col_lights_size.x, + col_lights_size.y, + gfx::texture::AaMode::Single, + ), + gfx::texture::Mipmap::Provided, + &[col_lights], + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + ) } } -pub struct PointShadowPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl PointShadowPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - terrain_layout: &TerrainLayout, - aa_mode: AaMode, - ) -> Self { - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Point shadow pipeline layout"), - push_constant_ranges: &[wgpu::PushConstantRange { - stages: wgpu::ShaderStage::all(), - range: 0..64, - }], - bind_group_layouts: &[&global_layout.globals, &terrain_layout.locals], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Point shadow pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[TerrainVertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth24Plus, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: None, - }); - - Self { - pipeline: render_pipeline, - } - } + +impl Pipeline for ShadowPipeline { + type Vertex = terrain::Vertex; } diff --git a/voxygen/src/render/pipelines/skybox.rs b/voxygen/src/render/pipelines/skybox.rs index 76dc159614..9d2b43be3a 100644 --- a/voxygen/src/render/pipelines/skybox.rs +++ b/voxygen/src/render/pipelines/skybox.rs @@ -1,120 +1,53 @@ -use super::super::{AaMode, GlobalsLayouts, Mesh, Quad, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::{ + super::{Mesh, Pipeline, Quad, TgtColorFmt, TgtDepthStencilFmt}, + Globals, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, +}; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pub pos: [f32; 3], -} +gfx_defines! { + vertex Vertex { + pos: [f32; 3] = "v_pos", + } -impl Vertex { - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x3, - }], - } + constant Locals { + nul: [f32; 4] = "nul", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), } } -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = None; - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; +impl Locals { + pub fn default() -> Self { Self { nul: [0.0; 4] } } } -// TODO: does skybox still do anything with new cloud shaders? -pub struct SkyboxPipeline { - pub pipeline: wgpu::RenderPipeline, +pub struct SkyboxPipeline; + +impl Pipeline for SkyboxPipeline { + type Vertex = Vertex; } -impl SkyboxPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - layouts: &GlobalsLayouts, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "SkyboxPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Skybox pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&layouts.globals, &layouts.shadow_textures], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Skybox pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} - -#[rustfmt::skip] -pub fn create_mesh() -> Mesh { +pub fn create_mesh() -> Mesh { let mut mesh = Mesh::new(); // -x + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [-1.0, -1.0, -1.0] }, Vertex { pos: [-1.0, 1.0, -1.0] }, @@ -122,6 +55,7 @@ pub fn create_mesh() -> Mesh { Vertex { pos: [-1.0, -1.0, 1.0] }, )); // +x + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [ 1.0, -1.0, 1.0] }, Vertex { pos: [ 1.0, 1.0, 1.0] }, @@ -129,6 +63,7 @@ pub fn create_mesh() -> Mesh { Vertex { pos: [ 1.0, -1.0, -1.0] }, )); // -y + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [ 1.0, -1.0, -1.0] }, Vertex { pos: [-1.0, -1.0, -1.0] }, @@ -136,6 +71,7 @@ pub fn create_mesh() -> Mesh { Vertex { pos: [ 1.0, -1.0, 1.0] }, )); // +y + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [ 1.0, 1.0, 1.0] }, Vertex { pos: [-1.0, 1.0, 1.0] }, @@ -143,6 +79,7 @@ pub fn create_mesh() -> Mesh { Vertex { pos: [ 1.0, 1.0, -1.0] }, )); // -z + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [-1.0, -1.0, -1.0] }, Vertex { pos: [ 1.0, -1.0, -1.0] }, @@ -150,6 +87,7 @@ pub fn create_mesh() -> Mesh { Vertex { pos: [-1.0, 1.0, -1.0] }, )); // +z + #[rustfmt::skip] mesh.push_quad(Quad::new( Vertex { pos: [-1.0, 1.0, 1.0] }, Vertex { pos: [ 1.0, 1.0, 1.0] }, diff --git a/voxygen/src/render/pipelines/sprite.rs b/voxygen/src/render/pipelines/sprite.rs index 6a98efcd96..21a8157585 100644 --- a/voxygen/src/render/pipelines/sprite.rs +++ b/voxygen/src/render/pipelines/sprite.rs @@ -1,49 +1,98 @@ use super::{ - super::{ - buffer::Buffer, AaMode, GlobalsLayouts, Mesh, TerrainLayout, Texture, Vertex as VertexTrait, - }, - lod_terrain, GlobalModel, + super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, terrain, Globals, Light, Shadow, +}; +use core::fmt; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask, }; -use bytemuck::{Pod, Zeroable}; -use std::mem; use vek::*; -pub const VERT_PAGE_SIZE: u32 = 256; +gfx_defines! { + vertex Vertex { + pos: [f32; 3] = "v_pos", + // Because we try to restrict terrain sprite data to a 128×128 block + // we need an offset into the texture atlas. + atlas_pos: u32 = "v_atlas_pos", + // ____BBBBBBBBGGGGGGGGRRRRRRRR + // col: u32 = "v_col", + // ...AANNN + // A = AO + // N = Normal + norm_ao: u32 = "v_norm_ao", + } -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pos_norm: u32, - // Because we try to restrict terrain sprite data to a 128×128 block - // we need an offset into the texture atlas. - atlas_pos: u32, - /* ____BBBBBBBBGGGGGGGGRRRRRRRR - * col: u32 = "v_col", - * .....NNN - * A = AO - * N = Normal - *norm: u32, */ + constant Locals { + // Each matrix performs rotatation, translation, and scaling, relative to the sprite + // origin, for all sprite instances. The matrix will be in an array indexed by the + // sprite instance's orientation (0 through 7). + mat: [[f32; 4]; 4] = "mat", + wind_sway: [f32; 4] = "wind_sway", + offs: [f32; 4] = "offs", + } + + vertex/*constant*/ Instance { + // Terrain block position and orientation + pos_ori: u32 = "inst_pos_ori", + inst_mat0: [f32; 4] = "inst_mat0", + inst_mat1: [f32; 4] = "inst_mat1", + inst_mat2: [f32; 4] = "inst_mat2", + inst_mat3: [f32; 4] = "inst_mat3", + inst_light: [f32; 4] = "inst_light", + inst_wind_sway: f32 = "inst_wind_sway", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + ibuf: gfx::InstanceBuffer = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", + + locals: gfx::ConstantBuffer = "u_locals", + // A sprite instance is a cross between a sprite and a terrain chunk. + terrain_locals: gfx::ConstantBuffer = "u_terrain_locals", + globals: gfx::ConstantBuffer = "u_globals", + lights: gfx::ConstantBuffer = "u_lights", + shadows: gfx::ConstantBuffer = "u_shadows", + + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_color: gfx::BlendTarget = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA), + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } } -// TODO: fix? -/*impl fmt::Display for Vertex { +impl fmt::Display for Vertex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Vertex") - .field("pos_norm", &Vec3::::from(self.pos)) + .field("pos", &Vec3::::from(self.pos)) .field( "atlas_pos", &Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF), ) + .field("norm_ao", &self.norm_ao) .finish() } -}*/ +} impl Vertex { // NOTE: Limit to 16 (x) × 16 (y) × 32 (z). #[allow(clippy::collapsible_else_if)] - pub fn new(atlas_pos: Vec2, pos: Vec3, norm: Vec3) -> Self { - const VERT_EXTRA_NEG_Z: i32 = 128; // NOTE: change if number of bits changes below, also we might not need this if meshing always produces positives values for sprites (I have no idea) - + pub fn new( + atlas_pos: Vec2, + pos: Vec3, + norm: Vec3, /* , col: Rgb, ao: f32 */ + ) -> Self { let norm_bits = if norm.x != 0.0 { if norm.x < 0.0 { 0 } else { 1 } } else if norm.y != 0.0 { @@ -58,287 +107,60 @@ impl Vertex { // | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12 // | if meta { 1 } else { 0 } << 28 // | (norm_bits & 0x7) << 29, - pos_norm: ((pos.x as u32) & 0x00FF) // NOTE: temp hack, this doesn't need 8 bits - | ((pos.y as u32) & 0x00FF) << 8 - | (((pos.z as i32 + VERT_EXTRA_NEG_Z).max(0).min(1 << 12) as u32) & 0x0FFF) << 16 - | (norm_bits & 0x7) << 29, + pos: pos.into_array(), atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16, + norm_ao: norm_bits, } } } -impl Default for Vertex { - fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) } -} - -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = Some(wgpu::IndexFormat::Uint16); - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; -} - -pub struct SpriteVerts(Buffer); -//pub struct SpriteVerts(Texture); - -pub(in super::super) fn create_verts_buffer( - device: &wgpu::Device, - mesh: Mesh, -) -> SpriteVerts { - // TODO: type Buffer by wgpu::BufferUsage - SpriteVerts(Buffer::new( - &device, - wgpu::BufferUsage::STORAGE, - mesh.vertices(), - )) -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Instance { - inst_mat0: [f32; 4], - inst_mat1: [f32; 4], - inst_mat2: [f32; 4], - inst_mat3: [f32; 4], - pos_ori: u32, - inst_vert_page: u32, - inst_light: f32, - inst_glow: f32, - model_wind_sway: f32, - model_z_scale: f32, -} - impl Instance { pub fn new( mat: Mat4, wind_sway: f32, - z_scale: f32, pos: Vec3, ori_bits: u8, light: f32, glow: f32, - vert_page: u32, ) -> Self { const EXTRA_NEG_Z: i32 = 32768; let mat_arr = mat.into_col_arrays(); Self { + pos_ori: ((pos.x as u32) & 0x003F) + | ((pos.y as u32) & 0x003F) << 6 + | (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12 + | (u32::from(ori_bits) & 0x7) << 29, inst_mat0: mat_arr[0], inst_mat1: mat_arr[1], inst_mat2: mat_arr[2], inst_mat3: mat_arr[3], - pos_ori: ((pos.x as u32) & 0x003F) - | ((pos.y as u32) & 0x003F) << 6 - | (((pos.z + EXTRA_NEG_Z).max(0).min(1 << 16) as u32) & 0xFFFF) << 12 - | (u32::from(ori_bits) & 0x7) << 29, - inst_vert_page: vert_page, - inst_light: light, - inst_glow: glow, - model_wind_sway: wind_sway, - model_z_scale: z_scale, - } - } - - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 10] = wgpu::vertex_attr_array![ - 0 => Float32x4, - 1 => Float32x4, - 2 => Float32x4, - 3 => Float32x4, - 4 => Uint32, - 5 => Uint32, - 6 => Float32, - 7 => Float32, - 8 => Float32, - 9 => Float32, - ]; - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::InputStepMode::Instance, - attributes: &ATTRIBUTES, + inst_light: [light, glow, 1.0, 1.0], + inst_wind_sway: wind_sway, } } } impl Default for Instance { - fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0, Vec3::zero(), 0, 1.0, 0.0, 0) } + fn default() -> Self { Self::new(Mat4::identity(), 0.0, Vec3::zero(), 0, 1.0, 0.0) } } -// TODO: ColLightsWrapper instead? -pub struct Locals; - -pub struct SpriteGlobalsBindGroup { - pub(in super::super) bind_group: wgpu::BindGroup, +impl Default for Locals { + fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) } } -pub struct SpriteLayout { - pub globals: wgpu::BindGroupLayout, -} - -impl SpriteLayout { - pub fn new(device: &wgpu::Device) -> Self { - let mut entries = GlobalsLayouts::base_globals_layout(); - debug_assert_eq!(12, entries.len()); // To remember to adjust the bindings below - entries.extend_from_slice(&[ - // sprite_verts - wgpu::BindGroupLayoutEntry { - binding: 12, - visibility: wgpu::ShaderStage::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: false, - min_binding_size: core::num::NonZeroU64::new( - core::mem::size_of::() as u64 - ), - }, - count: None, - }, - ]); - +impl Locals { + pub fn new(mat: Mat4, scale: Vec3, offs: Vec3, wind_sway: f32) -> Self { Self { - globals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &entries, - }), - } - } - - fn bind_globals_inner( - &self, - device: &wgpu::Device, - global_model: &GlobalModel, - lod_data: &lod_terrain::LodData, - noise: &Texture, - sprite_verts: &SpriteVerts, - ) -> wgpu::BindGroup { - let mut entries = GlobalsLayouts::bind_base_globals(global_model, lod_data, noise); - - entries.extend_from_slice(&[ - // sprite_verts - wgpu::BindGroupEntry { - binding: 12, - resource: sprite_verts.0.buf.as_entire_binding(), - }, - ]); - - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.globals, - entries: &entries, - }) - } - - pub fn bind_globals( - &self, - device: &wgpu::Device, - global_model: &GlobalModel, - lod_data: &lod_terrain::LodData, - noise: &Texture, - sprite_verts: &SpriteVerts, - ) -> SpriteGlobalsBindGroup { - let bind_group = - self.bind_globals_inner(device, global_model, lod_data, noise, sprite_verts); - - SpriteGlobalsBindGroup { bind_group } - } -} - -pub struct SpritePipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl SpritePipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - layout: &SpriteLayout, - terrain_layout: &TerrainLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "SpritePipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Sprite pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[ - &layout.globals, - &global_layout.shadow_textures, - &terrain_layout.locals, - // Note: mergable with globals - &global_layout.col_light, - ], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Sprite pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Instance::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - // TODO: can we remove sprite transparency? - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, + mat: mat.into_col_arrays(), + wind_sway: [scale.x, scale.y, scale.z, wind_sway], + offs: [offs.x, offs.y, offs.z, 0.0], } } } + +pub struct SpritePipeline; + +impl Pipeline for SpritePipeline { + type Vertex = Vertex; +} diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index 8a379f589d..0395f03f1f 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -1,13 +1,49 @@ -use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::{ + super::{ColLightFmt, Pipeline, TgtColorFmt, TgtDepthStencilFmt}, + shadow, Globals, Light, Shadow, +}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pos_norm: u32, - atlas_pos: u32, +gfx_defines! { + vertex Vertex { + pos_norm: u32 = "v_pos_norm", + atlas_pos: u32 = "v_atlas_pos", + } + + constant Locals { + model_offs: [f32; 3] = "model_offs", + load_time: f32 = "load_time", + atlas_offs: [i32; 4] = "atlas_offs", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light", + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + lights: gfx::ConstantBuffer = "u_lights", + shadows: gfx::ConstantBuffer = "u_shadows", + + point_shadow_maps: gfx::TextureSampler = "t_point_shadow_maps", + directed_shadow_maps: gfx::TextureSampler = "t_directed_shadow_maps", + + alt: gfx::TextureSampler<[f32; 2]> = "t_alt", + horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon", + + noise: gfx::TextureSampler = "t_noise", + + // Shadow stuff + light_shadows: gfx::ConstantBuffer = "u_light_shadows", + + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth_stencil: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + // tgt_depth_stencil: gfx::DepthStencilTarget = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))), + } } impl Vertex { @@ -68,7 +104,8 @@ impl Vertex { // 0 to 31 glow: u8, col: Rgb, - ) -> [u8; 4] { + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { //[col.r, col.g, col.b, light] // It would be nice for this to be cleaner, but we want to squeeze 5 fields into // 4. We can do this because both `light` and `glow` go from 0 to 31, @@ -104,7 +141,8 @@ impl Vertex { glowy: bool, shiny: bool, col: Rgb, - ) -> [u8; 4] { + ) -> <::Surface as gfx::format::SurfaceTyped>::DataType + { let attr = 0 | ((glowy as u8) << 0) | ((shiny as u8) << 1); [ (light.min(31) << 3) | ((col.r >> 1) & 0b111), @@ -113,48 +151,9 @@ impl Vertex { col.g, // Green is lucky, it remains unscathed ] } - - /// Set the bone_idx for an existing figure vertex. - pub fn set_bone_idx(&mut self, bone_idx: u8) { - self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27); - } - - pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Uint32,1 => Uint32]; - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &ATTRIBUTES, - } - } -} - -impl VertexTrait for Vertex { - // Note: I think it's u32 due to figures?? - // potentiall optimize by splitting - const QUADS_INDEX: Option = Some(wgpu::IndexFormat::Uint32); - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -// TODO: new function and private fields?? -pub struct Locals { - model_offs: [f32; 3], - load_time: f32, - atlas_offs: [i32; 4], } impl Locals { - pub fn new(model_offs: Vec3, atlas_offs: Vec2, load_time: f32) -> Self { - Self { - model_offs: model_offs.into_array(), - load_time, - atlas_offs: Vec4::new(atlas_offs.x as i32, atlas_offs.y as i32, 0, 0).into_array(), - } - } - pub fn default() -> Self { Self { model_offs: [0.0; 3], @@ -164,135 +163,8 @@ impl Locals { } } -pub type BoundLocals = Bound>; +pub struct TerrainPipeline; -pub struct TerrainLayout { - pub locals: wgpu::BindGroupLayout, -} - -impl TerrainLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // locals - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts) -> BoundLocals { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.locals, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: locals.buf().as_entire_binding(), - }], - }); - - BoundLocals { - bind_group, - with: locals, - } - } -} - -pub struct TerrainPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl TerrainPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - global_layout: &GlobalsLayouts, - layout: &TerrainLayout, - aa_mode: AaMode, - ) -> Self { - common_base::span!(_guard, "TerrainPipeline::new"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Terrain pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[ - &global_layout.globals, - &global_layout.shadow_textures, - &layout.locals, - &global_layout.col_light, - ], - }); - - let samples = match aa_mode { - AaMode::None | AaMode::Fxaa => 1, - AaMode::MsaaX4 => 4, - AaMode::MsaaX8 => 8, - AaMode::MsaaX16 => 16, - }; - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Terrain pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::GreaterEqual, - stencil: wgpu::StencilState { - front: wgpu::StencilFaceState::IGNORE, - back: wgpu::StencilFaceState::IGNORE, - read_mask: !0, - write_mask: !0, - }, - bias: wgpu::DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - }), - multisample: wgpu::MultisampleState { - count: samples, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: wgpu::TextureFormat::Rgba16Float, - blend: None, - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } +impl Pipeline for TerrainPipeline { + type Vertex = Vertex; } diff --git a/voxygen/src/render/pipelines/ui.rs b/voxygen/src/render/pipelines/ui.rs index 26e7e21de1..8a39625c6e 100644 --- a/voxygen/src/render/pipelines/ui.rs +++ b/voxygen/src/render/pipelines/ui.rs @@ -1,38 +1,41 @@ -use super::super::{Bound, Consts, GlobalsLayouts, Quad, Texture, Tri, Vertex as VertexTrait}; -use bytemuck::{Pod, Zeroable}; -use std::mem; +use super::super::{Globals, Pipeline, Quad, Tri, WinColorFmt, WinDepthFmt}; +use gfx::{ + self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, + gfx_pipeline_inner, gfx_vertex_struct_meta, +}; use vek::*; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Vertex { - pos: [f32; 2], - uv: [f32; 2], - color: [f32; 4], - center: [f32; 2], - mode: u32, -} +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + uv: [f32; 2] = "v_uv", + color: [f32; 4] = "v_color", + center: [f32; 2] = "v_center", + mode: u32 = "v_mode", + } -impl Vertex { - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 5] = wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Float32x4, 3 => Float32x2, 4 => Uint32]; - wgpu::VertexBufferLayout { - array_stride: Self::STRIDE, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &ATTRIBUTES, - } + constant Locals { + pos: [f32; 4] = "w_pos", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + tex: gfx::TextureSampler<[f32; 4]> = "u_tex", + + scissor: gfx::Scissor = (), + + tgt_color: gfx::BlendTarget = ("tgt_color", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA), + tgt_depth: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_TEST, } } -impl VertexTrait for Vertex { - const QUADS_INDEX: Option = None; - const STRIDE: wgpu::BufferAddress = mem::size_of::() as wgpu::BufferAddress; -} +pub struct UiPipeline; -#[repr(C)] -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct Locals { - pos: [f32; 4], +impl Pipeline for UiPipeline { + type Vertex = Vertex; } impl From> for Locals { @@ -84,177 +87,12 @@ impl Mode { } } -pub type BoundLocals = Bound>; - -pub struct TextureBindGroup { - pub(in super::super) bind_group: wgpu::BindGroup, -} - -pub struct UiLayout { - pub locals: wgpu::BindGroupLayout, - pub texture: wgpu::BindGroupLayout, -} - -impl UiLayout { - pub fn new(device: &wgpu::Device) -> Self { - Self { - locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // locals - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }), - texture: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - // texture - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - ], - }), - } - } - - pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts) -> BoundLocals { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.locals, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: locals.buf().as_entire_binding(), - }], - }); - - BoundLocals { - bind_group, - with: locals, - } - } - - pub fn bind_texture(&self, device: &wgpu::Device, texture: &Texture) -> TextureBindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.texture, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&texture.sampler), - }, - ], - }); - - TextureBindGroup { bind_group } - } -} - -pub struct UiPipeline { - pub pipeline: wgpu::RenderPipeline, -} - -impl UiPipeline { - pub fn new( - device: &wgpu::Device, - vs_module: &wgpu::ShaderModule, - fs_module: &wgpu::ShaderModule, - sc_desc: &wgpu::SwapChainDescriptor, - global_layout: &GlobalsLayouts, - layout: &UiLayout, - ) -> Self { - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Ui pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&global_layout.globals, &layout.locals, &layout.texture], - }); - - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("UI pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: vs_module, - entry_point: "main", - buffers: &[Vertex::desc()], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - clamp_depth: false, - polygon_mode: wgpu::PolygonMode::Fill, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - fragment: Some(wgpu::FragmentState { - module: fs_module, - entry_point: "main", - targets: &[wgpu::ColorTargetState { - format: sc_desc.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrite::ALL, - }], - }), - }); - - Self { - pipeline: render_pipeline, - } - } -} - pub fn create_quad( rect: Aabr, uv_rect: Aabr, color: Rgba, mode: Mode, -) -> Quad { +) -> Quad { create_quad_vert_gradient(rect, uv_rect, color, color, mode) } @@ -265,7 +103,7 @@ pub fn create_quad_vert_gradient( top_color: Rgba, bottom_color: Rgba, mode: Mode, -) -> Quad { +) -> Quad { let top_color = top_color.into_array(); let bottom_color = bottom_color.into_array(); @@ -314,7 +152,7 @@ pub fn create_tri( uv_tri: [[f32; 2]; 3], color: Rgba, mode: Mode, -) -> Tri { +) -> Tri { let center = [0.0, 0.0]; let mode_val = mode.value(); let v = |pos, uv| Vertex { diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 6870c32e42..d83048baa7 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -1,98 +1,241 @@ -mod binding; -pub(super) mod drawer; -// Consts and bind groups for post-process and clouds -mod locals; -mod pipeline_creation; -mod screenshot; -mod shaders; -mod shadow_map; - -use locals::Locals; -use pipeline_creation::{ - IngameAndShadowPipelines, InterfacePipelines, PipelineCreation, Pipelines, ShadowPipelines, -}; -use shaders::Shaders; -use shadow_map::{ShadowMap, ShadowMapRenderer}; - use super::{ - buffer::Buffer, consts::Consts, + gfx_backend, instances::Instances, mesh::Mesh, model::{DynamicModel, Model}, pipelines::{ - blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup, - GlobalsLayouts, ShadowTexturesBindGroup, + clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, + ui, GlobalModel, Globals, }, texture::Texture, - AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, + AaMode, CloudMode, FilterMethod, FluidMode, LightingMode, Pipeline, RenderError, RenderMode, + ShadowMapMode, ShadowMode, WrapMode, }; use common::assets::{self, AssetExt, AssetHandle}; use common_base::span; use core::convert::TryFrom; -use std::sync::Arc; -use tracing::{error, info, warn}; +use gfx::{ + self, + handle::Sampler, + state::Comparison, + traits::{Device, Factory, FactoryExt}, +}; +use glsl_include::Context as IncludeContext; +use tracing::{error, warn}; use vek::*; -// TODO: yeet this somewhere else +/// Represents the format of the pre-processed color target. +// TODO: `(gfx::format::R11_G11_B10, gfx::format::Float)` would be better in +// theory, but it doesn't seem to work +pub type TgtColorFmt = gfx::format::Rgba16F; +/// Represents the format of the pre-processed depth and stencil target. +pub type TgtDepthStencilFmt = gfx::format::Depth; + +/// Represents the format of the window's color target. +pub type WinColorFmt = gfx::format::Srgba8; +/// Represents the format of the window's depth target. +pub type WinDepthFmt = gfx::format::Depth; + +/// Represents the format of the pre-processed shadow depth target. +pub type ShadowDepthStencilFmt = gfx::format::Depth; + +/// A handle to a pre-processed color target. +pub type TgtColorView = gfx::handle::RenderTargetView; +/// A handle to a pre-processed depth target. +pub type TgtDepthStencilView = + gfx::handle::DepthStencilView; + +/// A handle to a window color target. +pub type WinColorView = gfx::handle::RenderTargetView; +/// A handle to a window depth target. +pub type WinDepthView = gfx::handle::DepthStencilView; + +/// Represents the format of LOD shadows. +pub type LodTextureFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Unorm); + +/// Represents the format of LOD altitudes. +pub type LodAltFmt = (gfx::format::R16_G16, gfx::format::Unorm); + +/// Represents the format of LOD map colors. +pub type LodColorFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); + +/// Represents the format of greedy meshed color-light textures. +pub type ColLightFmt = (gfx::format::R8_G8_B8_A8, gfx::format::Unorm); + +/// A handle to a shadow depth target. +pub type ShadowDepthStencilView = + gfx::handle::DepthStencilView; +/// A handle to a shadow depth target as a resource. +pub type ShadowResourceView = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + +/// A handle to a render color target as a resource. +pub type TgtColorRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + +/// A handle to a render depth target as a resource. +pub type TgtDepthRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + +/// A handle to a greedy meshed color-light texture as a resource. +pub type ColLightRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; /// A type representing data that can be converted to an immutable texture map /// of ColLight data (used for texture atlases created during greedy meshing). -// TODO: revert to u16 -pub type ColLightInfo = (Vec<[u8; 4]>, Vec2); +pub type ColLightInfo = ( + Vec<<::Surface as gfx::format::SurfaceTyped>::DataType>, + Vec2, +); -const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; -const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; +/// Load from a GLSL file. +pub struct Glsl(String); -/// A type that stores all the layouts associated with this renderer. -struct Layouts { - global: GlobalsLayouts, - - clouds: clouds::CloudsLayout, - debug: debug::DebugLayout, - figure: figure::FigureLayout, - postprocess: postprocess::PostProcessLayout, - shadow: shadow::ShadowLayout, - sprite: sprite::SpriteLayout, - terrain: terrain::TerrainLayout, - ui: ui::UiLayout, - blit: blit::BlitLayout, +impl From for Glsl { + fn from(s: String) -> Glsl { Glsl(s) } } -/// Render target views -struct Views { - // NOTE: unused for now, maybe... we will want it for something - _win_depth: wgpu::TextureView, +impl assets::Asset for Glsl { + type Loader = assets::LoadFrom; - tgt_color: wgpu::TextureView, - tgt_depth: wgpu::TextureView, - // TODO: rename - tgt_color_pp: wgpu::TextureView, + const EXTENSION: &'static str = "glsl"; } -/// Shadow rendering textures, layouts, pipelines, and bind groups -struct Shadow { - map: ShadowMap, - bind: ShadowTexturesBindGroup, +struct Shaders { + constants: AssetHandle, + globals: AssetHandle, + sky: AssetHandle, + light: AssetHandle, + srgb: AssetHandle, + random: AssetHandle, + lod: AssetHandle, + shadows: AssetHandle, + + anti_alias_none: AssetHandle, + anti_alias_fxaa: AssetHandle, + anti_alias_msaa_x4: AssetHandle, + anti_alias_msaa_x8: AssetHandle, + anti_alias_msaa_x16: AssetHandle, + cloud_none: AssetHandle, + cloud_regular: AssetHandle, + figure_vert: AssetHandle, + + terrain_point_shadow_vert: AssetHandle, + terrain_directed_shadow_vert: AssetHandle, + figure_directed_shadow_vert: AssetHandle, + directed_shadow_frag: AssetHandle, + + skybox_vert: AssetHandle, + skybox_frag: AssetHandle, + figure_frag: AssetHandle, + terrain_vert: AssetHandle, + terrain_frag: AssetHandle, + fluid_vert: AssetHandle, + fluid_frag_cheap: AssetHandle, + fluid_frag_shiny: AssetHandle, + sprite_vert: AssetHandle, + sprite_frag: AssetHandle, + particle_vert: AssetHandle, + particle_frag: AssetHandle, + ui_vert: AssetHandle, + ui_frag: AssetHandle, + lod_terrain_vert: AssetHandle, + lod_terrain_frag: AssetHandle, + clouds_vert: AssetHandle, + clouds_frag: AssetHandle, + postprocess_vert: AssetHandle, + postprocess_frag: AssetHandle, + player_shadow_frag: AssetHandle, + light_shadows_geom: AssetHandle, + light_shadows_frag: AssetHandle, } -/// Represent two states of the renderer: -/// 1. Only interface pipelines created -/// 2. All of the pipelines have been created -#[allow(clippy::large_enum_variant)] // They are both pretty large -enum State { - // NOTE: this is used as a transient placeholder for moving things out of State temporarily - Nothing, - Interface { - pipelines: InterfacePipelines, - shadow_views: Option<(Texture, Texture)>, - // In progress creation of the remaining pipelines in the background - creating: PipelineCreation, - }, - Complete { - pipelines: Pipelines, - shadow: Shadow, - recreating: Option>>, - }, +impl assets::Compound for Shaders { + // TODO: Taking the specifier argument as a base for shaders specifiers + // would allow to use several shaders groups easily + fn load( + _: &assets::AssetCache, + _: &str, + ) -> Result { + Ok(Shaders { + constants: AssetExt::load("voxygen.shaders.include.constants")?, + globals: AssetExt::load("voxygen.shaders.include.globals")?, + sky: AssetExt::load("voxygen.shaders.include.sky")?, + light: AssetExt::load("voxygen.shaders.include.light")?, + srgb: AssetExt::load("voxygen.shaders.include.srgb")?, + random: AssetExt::load("voxygen.shaders.include.random")?, + lod: AssetExt::load("voxygen.shaders.include.lod")?, + shadows: AssetExt::load("voxygen.shaders.include.shadows")?, + + anti_alias_none: AssetExt::load("voxygen.shaders.antialias.none")?, + anti_alias_fxaa: AssetExt::load("voxygen.shaders.antialias.fxaa")?, + anti_alias_msaa_x4: AssetExt::load("voxygen.shaders.antialias.msaa-x4")?, + anti_alias_msaa_x8: AssetExt::load("voxygen.shaders.antialias.msaa-x8")?, + anti_alias_msaa_x16: AssetExt::load("voxygen.shaders.antialias.msaa-x16")?, + cloud_none: AssetExt::load("voxygen.shaders.include.cloud.none")?, + cloud_regular: AssetExt::load("voxygen.shaders.include.cloud.regular")?, + figure_vert: AssetExt::load("voxygen.shaders.figure-vert")?, + + terrain_point_shadow_vert: AssetExt::load("voxygen.shaders.light-shadows-vert")?, + terrain_directed_shadow_vert: AssetExt::load( + "voxygen.shaders.light-shadows-directed-vert", + )?, + figure_directed_shadow_vert: AssetExt::load( + "voxygen.shaders.light-shadows-figure-vert", + )?, + directed_shadow_frag: AssetExt::load("voxygen.shaders.light-shadows-directed-frag")?, + + skybox_vert: AssetExt::load("voxygen.shaders.skybox-vert")?, + skybox_frag: AssetExt::load("voxygen.shaders.skybox-frag")?, + figure_frag: AssetExt::load("voxygen.shaders.figure-frag")?, + terrain_vert: AssetExt::load("voxygen.shaders.terrain-vert")?, + terrain_frag: AssetExt::load("voxygen.shaders.terrain-frag")?, + fluid_vert: AssetExt::load("voxygen.shaders.fluid-vert")?, + fluid_frag_cheap: AssetExt::load("voxygen.shaders.fluid-frag.cheap")?, + fluid_frag_shiny: AssetExt::load("voxygen.shaders.fluid-frag.shiny")?, + sprite_vert: AssetExt::load("voxygen.shaders.sprite-vert")?, + sprite_frag: AssetExt::load("voxygen.shaders.sprite-frag")?, + particle_vert: AssetExt::load("voxygen.shaders.particle-vert")?, + particle_frag: AssetExt::load("voxygen.shaders.particle-frag")?, + ui_vert: AssetExt::load("voxygen.shaders.ui-vert")?, + ui_frag: AssetExt::load("voxygen.shaders.ui-frag")?, + lod_terrain_vert: AssetExt::load("voxygen.shaders.lod-terrain-vert")?, + lod_terrain_frag: AssetExt::load("voxygen.shaders.lod-terrain-frag")?, + clouds_vert: AssetExt::load("voxygen.shaders.clouds-vert")?, + clouds_frag: AssetExt::load("voxygen.shaders.clouds-frag")?, + postprocess_vert: AssetExt::load("voxygen.shaders.postprocess-vert")?, + postprocess_frag: AssetExt::load("voxygen.shaders.postprocess-frag")?, + player_shadow_frag: AssetExt::load("voxygen.shaders.player-shadow-frag")?, + light_shadows_geom: AssetExt::load("voxygen.shaders.light-shadows-geom")?, + light_shadows_frag: AssetExt::load("voxygen.shaders.light-shadows-frag")?, + }) + } +} + +/// A type that holds shadow map data. Since shadow mapping may not be +/// supported on all platforms, we try to keep it separate. +pub struct ShadowMapRenderer { + // directed_encoder: gfx::Encoder, + // point_encoder: gfx::Encoder, + directed_depth_stencil_view: ShadowDepthStencilView, + directed_res: ShadowResourceView, + directed_sampler: Sampler, + + point_depth_stencil_view: ShadowDepthStencilView, + point_res: ShadowResourceView, + point_sampler: Sampler, + + point_pipeline: GfxPipeline>, + terrain_directed_pipeline: GfxPipeline>, + figure_directed_pipeline: GfxPipeline>, } /// A type that encapsulates rendering state. `Renderer` is central to Voxygen's @@ -100,160 +243,67 @@ enum State { /// GPU, along with pipeline state objects (PSOs) needed to renderer different /// kinds of models to the screen. pub struct Renderer { - device: Arc, - queue: wgpu::Queue, - surface: wgpu::Surface, - swap_chain: wgpu::SwapChain, - sc_desc: wgpu::SwapChainDescriptor, + device: gfx_backend::Device, + encoder: gfx::Encoder, + factory: gfx_backend::Factory, - sampler: wgpu::Sampler, - depth_sampler: wgpu::Sampler, + win_color_view: WinColorView, + win_depth_view: WinDepthView, - state: State, - // true if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader - // hotloading) - recreation_pending: bool, + tgt_color_view: TgtColorView, + tgt_depth_stencil_view: TgtDepthStencilView, + tgt_color_view_pp: TgtColorView, - layouts: Arc, - // Note: we keep these here since their bind groups need to be updated if we resize the - // color/depth textures - locals: Locals, - views: Views, - noise_tex: Texture, + tgt_color_res: TgtColorRes, + tgt_depth_res: TgtDepthRes, + tgt_color_res_pp: TgtColorRes, - quad_index_buffer_u16: Buffer, - quad_index_buffer_u32: Buffer, + sampler: Sampler, + + shadow_map: Option, + + skybox_pipeline: GfxPipeline>, + figure_pipeline: GfxPipeline>, + terrain_pipeline: GfxPipeline>, + fluid_pipeline: GfxPipeline>, + sprite_pipeline: GfxPipeline>, + particle_pipeline: GfxPipeline>, + ui_pipeline: GfxPipeline>, + lod_terrain_pipeline: GfxPipeline>, + clouds_pipeline: GfxPipeline>, + postprocess_pipeline: GfxPipeline>, + #[allow(dead_code)] //TODO: remove ? + player_shadow_pipeline: GfxPipeline>, shaders: AssetHandle, + noise_tex: Texture<(gfx::format::R8, gfx::format::Unorm)>, + mode: RenderMode, - resolution: Vec2, - - // If this is Some then a screenshot will be taken and passed to the handler here - take_screenshot: Option, - - profiler: wgpu_profiler::GpuProfiler, - profile_times: Vec, - profiler_features_enabled: bool, - - // This checks is added because windows resizes the window to 0,0 when - // minimizing and this causes a bunch of validation errors - is_minimized: bool, } impl Renderer { /// Create a new `Renderer` from a variety of backend-specific components /// and the window targets. - pub fn new(window: &winit::window::Window, mut mode: RenderMode) -> Result { + pub fn new( + mut device: gfx_backend::Device, + mut factory: gfx_backend::Factory, + win_color_view: WinColorView, + win_depth_view: WinDepthView, + mode: RenderMode, + ) -> Result { // Enable seamless cubemaps globally, where available--they are essentially a // strict improvement on regular cube maps. // // Note that since we only have to enable this once globally, there is no point // in doing this on rerender. - // Self::enable_seamless_cube_maps(&mut device); + Self::enable_seamless_cube_maps(&mut device); - // TODO: fix panic on wayland with opengl? - // TODO: fix backend defaulting to opengl on wayland. - let backend_bit = std::env::var("WGPU_BACKEND") - .ok() - .and_then(|backend| match backend.to_lowercase().as_str() { - "vulkan" => Some(wgpu::BackendBit::VULKAN), - "metal" => Some(wgpu::BackendBit::METAL), - "dx12" => Some(wgpu::BackendBit::DX12), - "primary" => Some(wgpu::BackendBit::PRIMARY), - "opengl" | "gl" => Some(wgpu::BackendBit::GL), - "dx11" => Some(wgpu::BackendBit::DX11), - "secondary" => Some(wgpu::BackendBit::SECONDARY), - "all" => Some(wgpu::BackendBit::all()), - _ => None, - }) - .unwrap_or( - (wgpu::BackendBit::PRIMARY | wgpu::BackendBit::SECONDARY) & !wgpu::BackendBit::GL, - ); + let dims = win_color_view.get_dimensions(); - let instance = wgpu::Instance::new(backend_bit); - - let dims = window.inner_size(); - - // This is unsafe because the window handle must be valid, if you find a way to - // have an invalid winit::Window then you have bigger issues - #[allow(unsafe_code)] - let surface = unsafe { instance.create_surface(window) }; - - let adapter = futures_executor::block_on(instance.request_adapter( - &wgpu::RequestAdapterOptionsBase { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - }, - )) - .ok_or(RenderError::CouldNotFindAdapter)?; - - let limits = wgpu::Limits { - max_push_constant_size: 64, - ..Default::default() - }; - - let (device, queue) = futures_executor::block_on( - adapter.request_device( - &wgpu::DeviceDescriptor { - // TODO - label: None, - features: wgpu::Features::DEPTH_CLAMPING - | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER - | wgpu::Features::PUSH_CONSTANTS - | (adapter.features() & wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES), - limits, - }, - std::env::var_os("WGPU_TRACE_DIR") - .as_ref() - .map(|v| std::path::Path::new(v)), - ), - )?; - - // Set error handler for wgpu errors - // This is better for use than their default because it includes the error in - // the panic message - device.on_uncaptured_error(|error| { - error!("{}", &error); - panic!( - "wgpu error (handling all wgpu errors as fatal): {:?}", - &error, - ) - }); - - let profiler_features_enabled = device - .features() - .contains(wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES); - if !profiler_features_enabled { - info!( - "The features for GPU profiling (timestamp queries) are not available on this \ - adapter" - ); - } - - let info = adapter.get_info(); - info!( - ?info.name, - ?info.vendor, - ?info.backend, - ?info.device, - ?info.device_type, - "selected graphics device" - ); - - let sc_desc = wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: dims.width, - height: dims.height, - present_mode: mode.present_mode.into(), - }; - - let swap_chain = device.create_swap_chain(&surface, &sc_desc); - - let shadow_views = ShadowMap::create_shadow_views( - &device, - (dims.width, dims.height), + let shadow_views = Self::create_shadow_views( + &mut factory, + (dims.0, dims.1), &ShadowMapMode::try_from(mode.shadow).unwrap_or_default(), ) .map_err(|err| { @@ -263,189 +313,157 @@ impl Renderer { let shaders = Shaders::load_expect(""); - let layouts = { - let global = GlobalsLayouts::new(&device); + let ( + skybox_pipeline, + figure_pipeline, + terrain_pipeline, + fluid_pipeline, + sprite_pipeline, + particle_pipeline, + ui_pipeline, + lod_terrain_pipeline, + clouds_pipeline, + postprocess_pipeline, + player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + ) = create_pipelines(&mut factory, &shaders.read(), &mode, shadow_views.is_some())?; - let clouds = clouds::CloudsLayout::new(&device); - let debug = debug::DebugLayout::new(&device); - let figure = figure::FigureLayout::new(&device); - let postprocess = postprocess::PostProcessLayout::new(&device); - let shadow = shadow::ShadowLayout::new(&device); - let sprite = sprite::SpriteLayout::new(&device); - let terrain = terrain::TerrainLayout::new(&device); - let ui = ui::UiLayout::new(&device); - let blit = blit::BlitLayout::new(&device); + let ( + tgt_color_view, + tgt_depth_stencil_view, + tgt_color_view_pp, + tgt_color_res, + tgt_depth_res, + tgt_color_res_pp, + ) = Self::create_rt_views(&mut factory, (dims.0, dims.1), &mode)?; - Layouts { - global, - - clouds, - debug, - figure, - postprocess, - shadow, - sprite, - terrain, - ui, - blit, - } - }; - - // Arcify the device and layouts - let device = Arc::new(device); - let layouts = Arc::new(layouts); - - let (interface_pipelines, creating) = pipeline_creation::initial_create_pipelines( - // TODO: combine Arcs? - Arc::clone(&device), - Arc::clone(&layouts), - shaders.read().clone(), - mode.clone(), - sc_desc.clone(), // Note: cheap clone - shadow_views.is_some(), - )?; - - let state = State::Interface { - pipelines: interface_pipelines, + let shadow_map = if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_views), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, shadow_views, - creating, - }; + ) { + let ( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + ) = shadow_views; + Some(ShadowMapRenderer { + directed_depth_stencil_view, + directed_res, + directed_sampler, - let views = Self::create_rt_views(&device, (dims.width, dims.height), &mode)?; + // point_encoder: factory.create_command_buffer().into(), + // directed_encoder: factory.create_command_buffer().into(), + point_depth_stencil_view, + point_res, + point_sampler, - let create_sampler = |filter| { - device.create_sampler(&wgpu::SamplerDescriptor { - label: None, - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: filter, - min_filter: filter, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: None, - ..Default::default() + point_pipeline, + terrain_directed_pipeline, + figure_directed_pipeline, }) + } else { + None }; - let sampler = create_sampler(wgpu::FilterMode::Linear); - let depth_sampler = create_sampler(wgpu::FilterMode::Nearest); + let sampler = factory.create_sampler(gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + )); let noise_tex = Texture::new( - &device, - &queue, + &mut factory, &assets::Image::load_expect("voxygen.texture.noise").read().0, - Some(wgpu::FilterMode::Linear), - Some(wgpu::AddressMode::Repeat), + Some(gfx::texture::FilterMethod::Trilinear), + Some(gfx::texture::WrapMode::Tile), + None, )?; - let clouds_locals = - Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]); - let postprocess_locals = - Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]); - - let locals = Locals::new( - &device, - &layouts, - clouds_locals, - postprocess_locals, - &views.tgt_color, - &views.tgt_depth, - &views.tgt_color_pp, - &sampler, - &depth_sampler, - ); - - let quad_index_buffer_u16 = - create_quad_index_buffer_u16(&device, QUAD_INDEX_BUFFER_U16_START_VERT_LEN as usize); - let quad_index_buffer_u32 = - create_quad_index_buffer_u32(&device, QUAD_INDEX_BUFFER_U32_START_VERT_LEN as usize); - let mut profiler = wgpu_profiler::GpuProfiler::new(4, queue.get_timestamp_period()); - mode.profiler_enabled &= profiler_features_enabled; - profiler.enable_timer = mode.profiler_enabled; - profiler.enable_debug_marker = mode.profiler_enabled; - Ok(Self { device, - queue, - surface, - swap_chain, - sc_desc, + encoder: factory.create_command_buffer().into(), + factory, - state, - recreation_pending: false, + win_color_view, + win_depth_view, - layouts, - locals, - views, + tgt_color_view, + tgt_depth_stencil_view, + tgt_color_view_pp, + + tgt_color_res, + tgt_depth_res, + tgt_color_res_pp, sampler, - depth_sampler, - noise_tex, - quad_index_buffer_u16, - quad_index_buffer_u32, + shadow_map, + + skybox_pipeline, + figure_pipeline, + terrain_pipeline, + fluid_pipeline, + sprite_pipeline, + particle_pipeline, + ui_pipeline, + lod_terrain_pipeline, + clouds_pipeline, + postprocess_pipeline, + player_shadow_pipeline, shaders, + noise_tex, + mode, - resolution: Vec2::new(dims.width, dims.height), - - take_screenshot: None, - - profiler, - profile_times: Vec::new(), - profiler_features_enabled, - - is_minimized: false, }) } - /// Check the status of the intial pipeline creation - /// Returns `None` if complete - /// Returns `Some((total, complete))` if in progress - pub fn pipeline_creation_status(&self) -> Option<(usize, usize)> { - if let State::Interface { creating, .. } = &self.state { - Some(creating.status()) - } else { - None - } + /// Get references to the internal render target views that get rendered to + /// before post-processing. + #[allow(dead_code)] + pub fn tgt_views(&self) -> (&TgtColorView, &TgtDepthStencilView) { + (&self.tgt_color_view, &self.tgt_depth_stencil_view) } - /// Check the status the pipeline recreation - /// Returns `None` if pipelines are currently not being recreated - /// Returns `Some((total, complete))` if in progress - pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> { - if let State::Complete { recreating, .. } = &self.state { - recreating.as_ref().map(|r| r.status()) - } else { - None - } + /// Get references to the internal render target views that get displayed + /// directly by the window. + #[allow(dead_code)] + pub fn win_views(&self) -> (&WinColorView, &WinDepthView) { + (&self.win_color_view, &self.win_depth_view) + } + + /// Get mutable references to the internal render target views that get + /// rendered to before post-processing. + #[allow(dead_code)] + pub fn tgt_views_mut(&mut self) -> (&mut TgtColorView, &mut TgtDepthStencilView) { + (&mut self.tgt_color_view, &mut self.tgt_depth_stencil_view) + } + + /// Get mutable references to the internal render target views that get + /// displayed directly by the window. + #[allow(dead_code)] + pub fn win_views_mut(&mut self) -> (&mut WinColorView, &mut WinDepthView) { + (&mut self.win_color_view, &mut self.win_depth_view) } /// Change the render mode. pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> { - // TODO: are there actually any issues with the current mode not matching the - // pipelines (since we could previously have inconsistencies from - // pipelines failing to build due to shader editing)? - // TODO: FIXME: defer mode changing until pipelines are rebuilt to prevent - // incompatibilities as pipelines are now rebuilt in a deferred mannder in the - // background TODO: consider separating changes that don't require - // rebuilding pipelines self.mode = mode; - self.sc_desc.present_mode = self.mode.present_mode.into(); - - // Only enable profiling if the wgpu features are enabled - self.mode.profiler_enabled &= self.profiler_features_enabled; - // Enable/disable profiler - if !self.mode.profiler_enabled { - // Clear the times if disabled - core::mem::take(&mut self.profile_times); - } - self.profiler.enable_timer = self.mode.profiler_enabled; - self.profiler.enable_debug_marker = self.mode.profiler_enabled; // Recreate render target - self.on_resize(self.resolution)?; + self.on_resize()?; // Recreate pipelines with the new AA mode self.recreate_pipelines(); @@ -456,241 +474,320 @@ impl Renderer { /// Get the render mode. pub fn render_mode(&self) -> &RenderMode { &self.mode } - /// Get the current profiling times - /// Nested timings immediately follow their parent - /// Returns Vec<(how nested this timing is, label, length in seconds)> - pub fn timings(&self) -> Vec<(u8, &str, f64)> { - use wgpu_profiler::GpuTimerScopeResult; - fn recursive_collect<'a>( - vec: &mut Vec<(u8, &'a str, f64)>, - result: &'a GpuTimerScopeResult, - nest_level: u8, - ) { - vec.push(( - nest_level, - &result.label, - result.time.end - result.time.start, - )); - result - .nested_scopes - .iter() - .for_each(|child| recursive_collect(vec, child, nest_level + 1)); - } - let mut vec = Vec::new(); - self.profile_times - .iter() - .for_each(|child| recursive_collect(&mut vec, child, 0)); - vec - } - /// Resize internal render targets to match window render target dimensions. - pub fn on_resize(&mut self, dims: Vec2) -> Result<(), RenderError> { + pub fn on_resize(&mut self) -> Result<(), RenderError> { + let dims = self.win_color_view.get_dimensions(); + // Avoid panics when creating texture with w,h of 0,0. - if dims.x != 0 && dims.y != 0 { - self.is_minimized = false; - // Resize swap chain - self.resolution = dims; - self.sc_desc.width = dims.x; - self.sc_desc.height = dims.y; - self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); - - // Resize other render targets - self.views = Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?; - // Rebind views to clouds/postprocess bind groups - self.locals.rebind( - &self.device, - &self.layouts, - &self.views.tgt_color, - &self.views.tgt_depth, - &self.views.tgt_color_pp, - &self.sampler, - &self.depth_sampler, - ); - - // Get mutable reference to shadow views out of the current state - let shadow_views = match &mut self.state { - State::Interface { shadow_views, .. } => { - shadow_views.as_mut().map(|s| (&mut s.0, &mut s.1)) - }, - State::Complete { - shadow: - Shadow { - map: ShadowMap::Enabled(shadow_map), - .. - }, - .. - } => Some((&mut shadow_map.point_depth, &mut shadow_map.directed_depth)), - State::Complete { .. } => None, - State::Nothing => None, // Should never hit this - }; - - if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) = - (shadow_views, self.mode.shadow) + if dims.0 != 0 && dims.1 != 0 { + let ( + tgt_color_view, + tgt_depth_stencil_view, + tgt_color_view_pp, + tgt_color_res, + tgt_depth_res, + tgt_color_res_pp, + ) = Self::create_rt_views(&mut self.factory, (dims.0, dims.1), &self.mode)?; + self.tgt_color_res = tgt_color_res; + self.tgt_depth_res = tgt_depth_res; + self.tgt_color_res_pp = tgt_color_res_pp; + self.tgt_color_view = tgt_color_view; + self.tgt_depth_stencil_view = tgt_depth_stencil_view; + self.tgt_color_view_pp = tgt_color_view_pp; + if let (Some(shadow_map), ShadowMode::Map(mode)) = + (self.shadow_map.as_mut(), self.mode.shadow) { - match ShadowMap::create_shadow_views(&self.device, (dims.x, dims.y), &mode) { - Ok((new_point_depth, new_directed_depth)) => { - *point_depth = new_point_depth; - *directed_depth = new_directed_depth; - // Recreate the shadow bind group if needed - if let State::Complete { - shadow: - Shadow { - bind, - map: ShadowMap::Enabled(shadow_map), - .. - }, - .. - } = &mut self.state - { - *bind = self.layouts.global.bind_shadow_textures( - &self.device, - &shadow_map.point_depth, - &shadow_map.directed_depth, - ); - } + match Self::create_shadow_views(&mut self.factory, (dims.0, dims.1), &mode) { + Ok(( + point_depth_stencil_view, + point_res, + point_sampler, + directed_depth_stencil_view, + directed_res, + directed_sampler, + )) => { + shadow_map.point_depth_stencil_view = point_depth_stencil_view; + shadow_map.point_res = point_res; + shadow_map.point_sampler = point_sampler; + + shadow_map.directed_depth_stencil_view = directed_depth_stencil_view; + shadow_map.directed_res = directed_res; + shadow_map.directed_sampler = directed_sampler; }, Err(err) => { warn!("Could not create shadow map views: {:?}", err); }, } } - } else { - self.is_minimized = true; } Ok(()) } - /// Create render target views fn create_rt_views( - device: &wgpu::Device, - size: (u32, u32), + factory: &mut gfx_device_gl::Factory, + size: (u16, u16), mode: &RenderMode, - ) -> Result { - let upscaled = Vec2::::from(size) - .map(|e| (e as f32 * mode.upscale_mode.factor) as u32) + ) -> Result< + ( + TgtColorView, + TgtDepthStencilView, + TgtColorView, + TgtColorRes, + TgtDepthRes, + TgtColorRes, + ), + RenderError, + > { + let upscaled = Vec2::from(size) + .map(|e: u16| (e as f32 * mode.upscale_mode.factor) as u16) .into_tuple(); - let (width, height, sample_count) = match mode.aa { - AaMode::None | AaMode::Fxaa => (upscaled.0, upscaled.1, 1), - AaMode::MsaaX4 => (upscaled.0, upscaled.1, 4), - AaMode::MsaaX8 => (upscaled.0, upscaled.1, 8), - AaMode::MsaaX16 => (upscaled.0, upscaled.1, 16), + let kind = match mode.aa { + AaMode::None | AaMode::Fxaa => { + gfx::texture::Kind::D2(upscaled.0, upscaled.1, gfx::texture::AaMode::Single) + }, + // TODO: Ensure sampling in the shader is exactly between the 4 texels + AaMode::MsaaX4 => { + gfx::texture::Kind::D2(upscaled.0, upscaled.1, gfx::texture::AaMode::Multi(4)) + }, + AaMode::MsaaX8 => { + gfx::texture::Kind::D2(upscaled.0, upscaled.1, gfx::texture::AaMode::Multi(8)) + }, + AaMode::MsaaX16 => { + gfx::texture::Kind::D2(upscaled.0, upscaled.1, gfx::texture::AaMode::Multi(16)) + }, }; let levels = 1; - let color_view = || { - let tex = device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: levels, - sample_count, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba16Float, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, - }); - - tex.create_view(&wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Rgba16Float), - dimension: Some(wgpu::TextureViewDimension::D2), - // TODO: why is this not Color? - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }) + let color_cty = <::Channel as gfx::format::ChannelTyped + >::get_channel_type(); + let mut color_tex = || { + factory.create_texture( + kind, + levels, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::RENDER_TARGET, + gfx::memory::Usage::Data, + Some(color_cty), + ) }; + let tgt_color_tex = color_tex()?; + let tgt_color_tex_pp = color_tex()?; + let mut color_res = |tex| { + factory.view_texture_as_shader_resource::( + tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + ) + }; + let tgt_color_res = color_res(&tgt_color_tex)?; + let tgt_color_res_pp = color_res(&tgt_color_tex_pp)?; + let tgt_color_view = factory.view_texture_as_render_target(&tgt_color_tex, 0, None)?; + let tgt_color_view_pp = + factory.view_texture_as_render_target(&tgt_color_tex_pp, 0, None)?; - let tgt_color_view = color_view(); - let tgt_color_pp_view = color_view(); + let depth_stencil_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); + let tgt_depth_stencil_tex = factory.create_texture( + kind, + levels, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + )?; + let tgt_depth_res = factory.view_texture_as_shader_resource::( + &tgt_depth_stencil_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + let tgt_depth_stencil_view = + factory.view_texture_as_depth_stencil_trivial(&tgt_depth_stencil_tex)?; - let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: levels, - sample_count, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth32Float, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, - }); - let tgt_depth_view = tgt_depth_tex.create_view(&wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Depth32Float), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, + Ok(( + tgt_color_view, + tgt_depth_stencil_view, + tgt_color_view_pp, + tgt_color_res, + tgt_depth_res, + tgt_color_res_pp, + )) + } + + /// Create textures and views for shadow maps. + // This is a one-use type and the two halves are not guaranteed to remain identical, so we + // disable the type complexity lint. + #[allow(clippy::type_complexity)] + fn create_shadow_views( + factory: &mut gfx_device_gl::Factory, + size: (u16, u16), + mode: &ShadowMapMode, + ) -> Result< + ( + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ShadowDepthStencilView, + ShadowResourceView, + Sampler, + ), + RenderError, + > { + // (Attempt to) apply resolution factor to shadow map resolution. + let resolution_factor = mode.resolution.clamped(0.25, 4.0); + + let max_texture_size = Self::max_texture_size_raw(factory); + // Limit to max texture size, rather than erroring. + let size = Vec2::new(size.0, size.1).map(|e| { + let size = f32::from(e) * resolution_factor; + // NOTE: We know 0 <= e since we clamped the resolution factor to be between + // 0.25 and 4.0. + if size <= f32::from(max_texture_size) { + size as u16 + } else { + max_texture_size + } }); - let win_depth_tex = device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: size.0, - height: size.1, - depth_or_array_layers: 1, - }, - mip_level_count: levels, - sample_count, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth32Float, - usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - }); - // TODO: Consider no depth buffer for the final draw to the window? - let win_depth_view = win_depth_tex.create_view(&wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Depth32Float), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, + let levels = 1; + // Limit to max texture size rather than erroring. + let two_size = size.map(|e| { + u16::checked_next_power_of_two(e) + .filter(|&e| e <= max_texture_size) + .unwrap_or(max_texture_size) }); + let min_size = size.reduce_min(); + let max_size = size.reduce_max(); + let _min_two_size = two_size.reduce_min(); + let _max_two_size = two_size.reduce_max(); + // For rotated shadow maps, the maximum size of a pixel along any axis is the + // size of a diagonal along that axis. + let diag_size = size.map(f64::from).magnitude(); + let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size; + let (diag_size, _diag_cross_size) = + if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) { + // NOTE: diag_cross_size must be non-negative, since it is the ratio of a + // non-negative and a positive number (if max_size were zero, + // diag_size would be 0 too). And it must be <= diag_size, + // since min_size <= max_size. Therefore, if diag_size fits in a + // u16, so does diag_cross_size. + (diag_size as u16, diag_cross_size as u16) + } else { + // Limit to max texture resolution rather than error. + (max_texture_size as u16, max_texture_size as u16) + }; + let diag_two_size = u16::checked_next_power_of_two(diag_size) + .filter(|&e| e <= max_texture_size) + // Limit to max texture resolution rather than error. + .unwrap_or(max_texture_size); + let depth_stencil_cty = <::Channel as gfx::format::ChannelTyped>::get_channel_type(); - Ok(Views { - tgt_color: tgt_color_view, - tgt_depth: tgt_depth_view, - tgt_color_pp: tgt_color_pp_view, - _win_depth: win_depth_view, - }) + let point_shadow_tex = factory + .create_texture( + gfx::texture::Kind::Cube(diag_two_size / 4), + levels as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + + let point_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &point_shadow_tex, + 0, + None, + gfx::texture::DepthStencilFlags::empty(), + )?; + + let point_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &point_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let directed_shadow_tex = factory + .create_texture( + gfx::texture::Kind::D2(diag_two_size, diag_two_size, gfx::texture::AaMode::Single), + levels as gfx::texture::Level, + gfx::memory::Bind::SHADER_RESOURCE | gfx::memory::Bind::DEPTH_STENCIL, + gfx::memory::Usage::Data, + Some(depth_stencil_cty), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + let directed_tgt_shadow_view = factory + .view_texture_as_depth_stencil::( + &directed_shadow_tex, + 0, + None, + gfx::texture::DepthStencilFlags::empty(), + )?; + let directed_tgt_shadow_res = factory + .view_texture_as_shader_resource::( + &directed_shadow_tex, + (0, levels - 1), + gfx::format::Swizzle::new(), + )?; + + let mut sampler_info = gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + // Lights should always be assumed to flood areas we can't see. + gfx::texture::WrapMode::Border, + ); + sampler_info.comparison = Some(Comparison::LessEqual); + sampler_info.border = [1.0; 4].into(); + let point_shadow_tex_sampler = factory.create_sampler(sampler_info); + let directed_shadow_tex_sampler = factory.create_sampler(sampler_info); + + Ok(( + point_tgt_shadow_view, + point_tgt_shadow_res, + point_shadow_tex_sampler, + directed_tgt_shadow_view, + directed_tgt_shadow_res, + directed_shadow_tex_sampler, + )) } /// Get the resolution of the render target. - pub fn resolution(&self) -> Vec2 { self.resolution } - - /// Get the resolution of the shadow render target. - pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { - match &self.state { - State::Interface { shadow_views, .. } => shadow_views.as_ref().map(|s| (&s.0, &s.1)), - State::Complete { - shadow: - Shadow { - map: ShadowMap::Enabled(shadow_map), - .. - }, - .. - } => Some((&shadow_map.point_depth, &shadow_map.directed_depth)), - State::Complete { .. } | State::Nothing => None, - } - .map(|(point, directed)| (point.get_dimensions().xy(), directed.get_dimensions().xy())) - .unwrap_or_else(|| (Vec2::new(1, 1), Vec2::new(1, 1))) + /// Note: the change after a resize can be delayed so + /// don't rely on this value being constant between resize events + pub fn get_resolution(&self) -> Vec2 { + Vec2::new( + self.win_color_view.get_dimensions().0, + self.win_color_view.get_dimensions().1, + ) + } + + /// Get the resolution of the shadow render target. + pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { + if let Some(shadow_map) = &self.shadow_map { + let point_dims = shadow_map.point_depth_stencil_view.get_dimensions(); + let directed_dims = shadow_map.directed_depth_stencil_view.get_dimensions(); + ( + Vec2::new(point_dims.0, point_dims.1), + Vec2::new(directed_dims.0, directed_dims.1), + ) + } else { + (Vec2::new(1, 1), Vec2::new(1, 1)) + } + } + + /// Queue the clearing of the shadow targets ready for a new frame to be + /// rendered. + pub fn clear_shadows(&mut self) { + span!(_guard, "clear_shadows", "Renderer::clear_shadows"); + if !self.mode.shadow.is_map() { + return; + } + if let Some(shadow_map) = self.shadow_map.as_mut() { + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.clear_depth(&shadow_map.point_depth_stencil_view, 1.0); + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.clear_depth(&shadow_map.directed_depth_stencil_view, 1.0); + } } - // TODO: Seamless is potentially the default with wgpu but we need further - // investigation into whether this is actually turned on for the OpenGL - // backend - // /// NOTE: Supported by Vulkan (by default), DirectX 10+ (it seems--it's hard /// to find proof of this, but Direct3D 10 apparently does it by /// default, and 11 definitely does, so I assume it's natively supported @@ -698,540 +795,1440 @@ impl Renderer { /// there may be some GPUs that don't quite support it correctly, the /// impact is relatively small, so there is no reason not to enable it where /// available. - //fn enable_seamless_cube_maps() { - //todo!() - // unsafe { - // // NOTE: Currently just fail silently rather than complain if the - // computer is on // a version lower than 3.2, where - // seamless cubemaps were introduced. if !device.get_info(). - // is_version_supported(3, 2) { return; - // } + #[allow(unsafe_code)] + fn enable_seamless_cube_maps(device: &mut gfx_backend::Device) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.2, where seamless cubemaps were introduced. + if !device.get_info().is_version_supported(3, 2) { + return; + } - // // NOTE: Safe because GL_TEXTURE_CUBE_MAP_SEAMLESS is supported - // by OpenGL 3.2+ // (see https://www.khronos.org/opengl/wiki/Cubemap_Texture#Seamless_cubemap); - // // enabling seamless cube maps should always be safe regardless - // of the state of // the OpenGL context, so no further - // checks are needed. device.with_gl(|gl| { - // gl.Enable(gfx_gl::TEXTURE_CUBE_MAP_SEAMLESS); - // }); - // } - //} - - /// Start recording the frame - /// When the returned `Drawer` is dropped the recorded draw calls will be - /// submitted to the queue - /// If there is an intermittent issue with the swap chain then Ok(None) will - /// be returned - pub fn start_recording_frame<'a>( - &'a mut self, - globals: &'a GlobalsBindGroup, - ) -> Result>, RenderError> { - span!( - _guard, - "start_recording_frame", - "Renderer::start_recording_frame" - ); - - if self.is_minimized { - return Ok(None); + // NOTE: Safe because GL_TEXTURE_CUBE_MAP_SEAMLESS is supported by OpenGL 3.2+ + // (see https://www.khronos.org/opengl/wiki/Cubemap_Texture#Seamless_cubemap); + // enabling seamless cube maps should always be safe regardless of the state of + // the OpenGL context, so no further checks are needed. + device.with_gl(|gl| { + gl.Enable(gfx_gl::TEXTURE_CUBE_MAP_SEAMLESS); + }); } + } - // Try to get the latest profiling results - if self.mode.profiler_enabled { - // Note: this lags a few frames behind - if let Some(profile_times) = self.profiler.process_finished_frame() { - self.profile_times = profile_times; + /// NOTE: Supported by all but a handful of mobile GPUs + /// (see https://github.com/gpuweb/gpuweb/issues/480) + /// so wgpu should support it too. + #[allow(unsafe_code)] + fn set_depth_clamp(device: &mut gfx_backend::Device, depth_clamp: bool) { + unsafe { + // NOTE: Currently just fail silently rather than complain if the computer is on + // a version lower than 3.3, though we probably will complain + // elsewhere regardless, since shadow mapping is an optional feature + // and having depth clamping disabled won't cause undefined + // behavior, just incorrect shadowing from objects behind the viewer. + if !device.get_info().is_version_supported(3, 3) { + return; } + + // NOTE: Safe because glDepthClamp is (I believe) supported by + // OpenGL 3.3, so we shouldn't have to check for other OpenGL versions which + // may use different extensions. Also, enabling depth clamping should + // essentially always be safe regardless of the state of the OpenGL + // context, so no further checks are needed. + device.with_gl(|gl| { + if depth_clamp { + gl.Enable(gfx_gl::DEPTH_CLAMP); + } else { + gl.Disable(gfx_gl::DEPTH_CLAMP); + } + }); } + } - // Handle polling background pipeline creation/recreation - // Temporarily set to nothing and then replace in the statement below - let state = core::mem::replace(&mut self.state, State::Nothing); - // If still creating initial pipelines, check if complete - self.state = if let State::Interface { - pipelines: interface, - shadow_views, - creating, - } = state - { - match creating.try_complete() { - Ok(pipelines) => { - let IngameAndShadowPipelines { ingame, shadow } = pipelines; + /// Queue the clearing of the depth target ready for a new frame to be + /// rendered. + pub fn clear(&mut self) { + span!(_guard, "clear", "Renderer::clear"); + self.encoder.clear_depth(&self.tgt_depth_stencil_view, 1.0); + // self.encoder.clear_stencil(&self.tgt_depth_stencil_view, 0); + self.encoder.clear_depth(&self.win_depth_view, 1.0); + } - let pipelines = Pipelines::consolidate(interface, ingame); + /// Set up shadow rendering. + pub fn start_shadows(&mut self) { + if !self.mode.shadow.is_map() { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + self.encoder.flush(&mut self.device); + Self::set_depth_clamp(&mut self.device, true); + } + } - let shadow_map = ShadowMap::new( - &self.device, - &self.queue, - shadow.point, - shadow.directed, - shadow.figure, - shadow_views, - ); + /// Perform all queued draw calls for global.shadows. + pub fn flush_shadows(&mut self) { + if !self.mode.shadow.is_map() { + return; + } + if let Some(_shadow_map) = self.shadow_map.as_mut() { + let point_encoder = &mut self.encoder; + // let point_encoder = &mut shadow_map.point_encoder; + point_encoder.flush(&mut self.device); + // let directed_encoder = &mut shadow_map.directed_encoder; + // directed_encoder.flush(&mut self.device); + // Reset depth clamping. + Self::set_depth_clamp(&mut self.device, false); + } + } - let shadow_bind = { - let (point, directed) = shadow_map.textures(); - self.layouts - .global - .bind_shadow_textures(&self.device, point, directed) - }; - - let shadow = Shadow { - map: shadow_map, - bind: shadow_bind, - }; - - State::Complete { - pipelines, - shadow, - recreating: None, - } - }, - // Not complete - Err(creating) => State::Interface { - pipelines: interface, - shadow_views, - creating, - }, - } - // If recreating the pipelines, check if that is complete - } else if let State::Complete { - pipelines, - mut shadow, - recreating: Some(recreating), - } = state - { - match recreating.try_complete() { - Ok(Ok((pipelines, shadow_pipelines))) => { - if let ( - Some(point_pipeline), - Some(terrain_directed_pipeline), - Some(figure_directed_pipeline), - ShadowMap::Enabled(shadow_map), - ) = ( - shadow_pipelines.point, - shadow_pipelines.directed, - shadow_pipelines.figure, - &mut shadow.map, - ) { - shadow_map.point_pipeline = point_pipeline; - shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; - shadow_map.figure_directed_pipeline = figure_directed_pipeline; - } - State::Complete { - pipelines, - shadow, - recreating: None, - } - }, - Ok(Err(e)) => { - error!(?e, "Could not recreate shaders from assets due to an error"); - State::Complete { - pipelines, - shadow, - recreating: None, - } - }, - // Not complete - Err(recreating) => State::Complete { - pipelines, - shadow, - recreating: Some(recreating), - }, - } - } else { - state - }; + /// Perform all queued draw calls for this frame and clean up discarded + /// items. + pub fn flush(&mut self) { + span!(_guard, "flush", "Renderer::flush"); + self.encoder.flush(&mut self.device); + self.device.cleanup(); // If the shaders files were changed attempt to recreate the shaders if self.shaders.reloaded() { self.recreate_pipelines(); } - - // Or if we have a recreation pending - if self.recreation_pending - && matches!(&self.state, State::Complete { recreating, .. } if recreating.is_none()) - { - self.recreation_pending = false; - self.recreate_pipelines(); - } - - let tex = match self.swap_chain.get_current_frame() { - Ok(frame) => frame.output, - // If lost recreate the swap chain - Err(err @ wgpu::SwapChainError::Lost) => { - warn!("{}. Recreating swap chain. A frame will be missed", err); - return self.on_resize(self.resolution).map(|()| None); - }, - Err(wgpu::SwapChainError::Timeout) => { - // This will probably be resolved on the next frame - // NOTE: we don't log this because it happens very frequently with - // PresentMode::Fifo and unlimited FPS on certain machines - return Ok(None); - }, - Err(err @ wgpu::SwapChainError::Outdated) => { - warn!("{}. Recreating the swapchain", err); - self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); - return Ok(None); - }, - Err(err @ wgpu::SwapChainError::OutOfMemory) => return Err(err.into()), - }; - let encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("A render encoder"), - }); - - Ok(Some(drawer::Drawer::new(encoder, self, tex, globals))) } /// Recreate the pipelines fn recreate_pipelines(&mut self) { - match &mut self.state { - State::Complete { recreating, .. } if recreating.is_some() => { - // Defer recreation so that we are not building multiple sets of pipelines in - // the background at once - self.recreation_pending = true; + match create_pipelines( + &mut self.factory, + &self.shaders.read(), + &self.mode, + self.shadow_map.is_some(), + ) { + Ok(( + skybox_pipeline, + figure_pipeline, + terrain_pipeline, + fluid_pipeline, + sprite_pipeline, + particle_pipeline, + ui_pipeline, + lod_terrain_pipeline, + clouds_pipeline, + postprocess_pipeline, + player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + )) => { + self.skybox_pipeline = skybox_pipeline; + self.figure_pipeline = figure_pipeline; + self.terrain_pipeline = terrain_pipeline; + self.fluid_pipeline = fluid_pipeline; + self.sprite_pipeline = sprite_pipeline; + self.particle_pipeline = particle_pipeline; + self.ui_pipeline = ui_pipeline; + self.lod_terrain_pipeline = lod_terrain_pipeline; + self.clouds_pipeline = clouds_pipeline; + self.postprocess_pipeline = postprocess_pipeline; + self.player_shadow_pipeline = player_shadow_pipeline; + if let ( + Some(point_pipeline), + Some(terrain_directed_pipeline), + Some(figure_directed_pipeline), + Some(shadow_map), + ) = ( + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + self.shadow_map.as_mut(), + ) { + shadow_map.point_pipeline = point_pipeline; + shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; + shadow_map.figure_directed_pipeline = figure_directed_pipeline; + } }, - State::Complete { - recreating, shadow, .. - } => { - *recreating = Some(pipeline_creation::recreate_pipelines( - Arc::clone(&self.device), - Arc::clone(&self.layouts), - self.shaders.read().clone(), - self.mode.clone(), - self.sc_desc.clone(), // Note: cheap clone - shadow.map.is_enabled(), - )); - }, - State::Interface { .. } => { - // Defer recreation so that we are not building multiple sets of pipelines in - // the background at once - self.recreation_pending = true; - }, - State::Nothing => {}, + Err(e) => error!(?e, "Could not recreate shaders from assets due to an error",), } } /// Create a new set of constants with the provided values. - pub fn create_consts(&mut self, vals: &[T]) -> Consts { - Self::create_consts_inner(&self.device, &self.queue, vals) - } - - pub fn create_consts_inner( - device: &wgpu::Device, - queue: &wgpu::Queue, + pub fn create_consts( + &mut self, vals: &[T], - ) -> Consts { - let mut consts = Consts::new(device, vals.len()); - consts.update(queue, vals, 0); - consts + ) -> Result, RenderError> { + let mut consts = Consts::new(&mut self.factory, vals.len()); + consts.update(&mut self.encoder, vals, 0)?; + Ok(consts) } /// Update a set of constants with the provided values. - pub fn update_consts(&self, consts: &mut Consts, vals: &[T]) { - consts.update(&self.queue, vals, 0) - } - - pub fn update_clouds_locals(&mut self, new_val: clouds::Locals) { - self.locals.clouds.update(&self.queue, &[new_val], 0) - } - - pub fn update_postprocess_locals(&mut self, new_val: postprocess::Locals) { - self.locals.postprocess.update(&self.queue, &[new_val], 0) + pub fn update_consts( + &mut self, + consts: &mut Consts, + vals: &[T], + ) -> Result<(), RenderError> { + consts.update(&mut self.encoder, vals, 0) } /// Create a new set of instances with the provided values. - pub fn create_instances( + pub fn create_instances( &mut self, vals: &[T], ) -> Result, RenderError> { - let mut instances = Instances::new(&self.device, vals.len()); - instances.update(&self.queue, vals, 0); + let mut instances = Instances::new(&mut self.factory, vals.len())?; + instances.update(&mut self.encoder, vals)?; Ok(instances) } - /// Ensure that the quad index buffer is large enough for a quad vertex - /// buffer with this many vertices - pub(super) fn ensure_sufficient_index_length( - &mut self, - // Length of the vert buffer with 4 verts per quad - vert_length: usize, - ) { - let quad_index_length = vert_length / 4 * 6; - - match V::QUADS_INDEX { - Some(wgpu::IndexFormat::Uint16) => { - // Make sure the global quad index buffer is large enough - if self.quad_index_buffer_u16.len() < quad_index_length { - // Make sure we aren't over the max - if vert_length > u16::MAX as usize { - panic!( - "Vertex type: {} needs to use a larger index type, length: {}", - core::any::type_name::(), - vert_length - ); - } - self.quad_index_buffer_u16 = - create_quad_index_buffer_u16(&self.device, vert_length); - } - }, - Some(wgpu::IndexFormat::Uint32) => { - // Make sure the global quad index buffer is large enough - if self.quad_index_buffer_u32.len() < quad_index_length { - // Make sure we aren't over the max - if vert_length > u32::MAX as usize { - panic!( - "More than u32::MAX({}) verts({}) for type({}) using an index buffer!", - u32::MAX, - vert_length, - core::any::type_name::() - ); - } - self.quad_index_buffer_u32 = - create_quad_index_buffer_u32(&self.device, vert_length); - } - }, - None => {}, - } - } - - pub fn create_sprite_verts(&mut self, mesh: Mesh) -> sprite::SpriteVerts { - self.ensure_sufficient_index_length::(sprite::VERT_PAGE_SIZE as usize); - sprite::create_verts_buffer(&self.device, mesh) - } - /// Create a new model from the provided mesh. - /// If the provided mesh is empty this returns None - pub fn create_model(&mut self, mesh: &Mesh) -> Option> { - self.ensure_sufficient_index_length::(mesh.vertices().len()); - Model::new(&self.device, mesh) + pub fn create_model(&mut self, mesh: &Mesh

) -> Result, RenderError> { + Ok(Model::new(&mut self.factory, mesh)) } /// Create a new dynamic model with the specified size. - pub fn create_dynamic_model(&mut self, size: usize) -> DynamicModel { - DynamicModel::new(&self.device, size) + pub fn create_dynamic_model( + &mut self, + size: usize, + ) -> Result, RenderError> { + DynamicModel::new(&mut self.factory, size) } /// Update a dynamic model with a mesh and a offset. - pub fn update_model(&self, model: &DynamicModel, mesh: &Mesh, offset: usize) { - model.update(&self.queue, mesh, offset) + pub fn update_model( + &mut self, + model: &DynamicModel

, + mesh: &Mesh

, + offset: usize, + ) -> Result<(), RenderError> { + model.update(&mut self.encoder, mesh, offset) } /// Return the maximum supported texture size. - pub fn max_texture_size(&self) -> u32 { Self::max_texture_size_raw(&self.device) } + pub fn max_texture_size(&self) -> u16 { Self::max_texture_size_raw(&self.factory) } /// Return the maximum supported texture size from the factory. - fn max_texture_size_raw(_device: &wgpu::Device) -> u32 { - // This value is temporary as there are plans to include a way to get this in - // wgpu this is just a sane standard for now - 8192 + fn max_texture_size_raw(factory: &gfx_backend::Factory) -> u16 { + /// NOTE: OpenGL requirement. + const MAX_TEXTURE_SIZE_MIN: u16 = 1024; + #[cfg(target_os = "macos")] + /// NOTE: Because Macs lie about their max supported texture size. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + #[cfg(not(target_os = "macos"))] + /// NOTE: Apparently Macs aren't the only machines that lie. + /// + /// TODO: Find a way to let graphics cards that don't lie do better. + const MAX_TEXTURE_SIZE_MAX: u16 = 8192; + // NOTE: Many APIs for textures require coordinates to fit in u16, which is why + // we perform this conversion. + u16::try_from(factory.get_capabilities().max_texture_size) + .unwrap_or(MAX_TEXTURE_SIZE_MIN) + .min(MAX_TEXTURE_SIZE_MAX) } /// Create a new immutable texture from the provided image. - /// # Panics - /// If the provided data doesn't completely fill the texture this function - /// will panic. - pub fn create_texture_with_data_raw( + pub fn create_texture_immutable_raw( &mut self, - texture_info: &wgpu::TextureDescriptor, - view_info: &wgpu::TextureViewDescriptor, - sampler_info: &wgpu::SamplerDescriptor, - data: &[u8], - ) -> Texture { - let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info); - - let size = texture_info.size; - let block_size = texture_info.format.describe().block_size; - assert_eq!( - size.width as usize - * size.height as usize - * size.depth_or_array_layers as usize - * block_size as usize, - data.len(), - "Provided data length {} does not fill the provided texture size {:?}", - data.len(), - size, - ); - - tex.update( - &self.queue, - [0; 2], - [texture_info.size.width, texture_info.size.height], - data, - ); - - tex + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_immutable_raw(&mut self.factory, kind, mipmap, data, sampler_info) } /// Create a new raw texture. - pub fn create_texture_raw( + pub fn create_texture_raw( &mut self, - texture_info: &wgpu::TextureDescriptor, - view_info: &wgpu::TextureViewDescriptor, - sampler_info: &wgpu::SamplerDescriptor, - ) -> Texture { - let texture = Texture::new_raw(&self.device, texture_info, view_info, sampler_info); - texture.clear(&self.queue); // Needs to be fully initialized for partial writes to work on Dx12 AMD - texture - } - - /// Create a new texture from the provided image. - /// - /// Currently only supports Rgba8Srgb - pub fn create_texture( - &mut self, - image: &image::DynamicImage, - filter_method: Option, - address_mode: Option, - ) -> Result { - Texture::new( - &self.device, - &self.queue, - image, - filter_method, - address_mode, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new_raw( + &mut self.device, + &mut self.factory, + kind, + max_levels, + bind, + usage, + levels, + swizzle, + sampler_info, ) } - /// Create a new dynamic texture with the + /// Create a new texture from the provided image. + pub fn create_texture( + &mut self, + image: &image::DynamicImage, + filter_method: Option, + wrap_mode: Option, + border: Option, + ) -> Result, RenderError> + where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + Texture::new(&mut self.factory, image, filter_method, wrap_mode, border) + } + + /// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the /// specified dimensions. - /// - /// Currently only supports Rgba8Srgb - pub fn create_dynamic_texture(&mut self, dims: Vec2) -> Texture { - Texture::new_dynamic(&self.device, &self.queue, dims.x, dims.y) + pub fn create_dynamic_texture(&mut self, dims: Vec2) -> Result { + Texture::new_dynamic(&mut self.factory, dims.x, dims.y) } /// Update a texture with the provided offset, size, and data. - /// - /// Currently only supports Rgba8Srgb - pub fn update_texture( + pub fn update_texture( &mut self, - texture: &Texture, /* */ - offset: [u32; 2], - size: [u32; 2], - // TODO: be generic over pixel type - data: &[[u8; 4]], - ) { - texture.update(&self.queue, offset, size, bytemuck::cast_slice(data)) + texture: &Texture, + offset: [u16; 2], + size: [u16; 2], + data: &[<::Surface as gfx::format::SurfaceTyped>::DataType], + ) -> Result<(), RenderError> + where + ::Surface: gfx::format::TextureSurface, + ::Channel: gfx::format::TextureChannel, + <::Surface as gfx::format::SurfaceTyped>::DataType: Copy, + { + texture.update(&mut self.encoder, offset, size, data) } - /// Queue to obtain a screenshot on the next frame render - pub fn create_screenshot( - &mut self, - screenshot_handler: impl FnOnce(image::DynamicImage) + Send + 'static, - ) { - // Queue screenshot - self.take_screenshot = Some(Box::new(screenshot_handler)); - // Take profiler snapshot - if self.mode.profiler_enabled { - let file_name = format!( - "frame-trace_{}.json", - std::time::SystemTime::now() - .duration_since(std::time::SystemTime::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0) - ); + /// Creates a download buffer, downloads the win_color_view, and converts to + /// a image::DynamicImage. + #[allow(clippy::map_clone)] // TODO: Pending review in #587 + pub fn create_screenshot(&mut self) -> Result { + let (width, height) = self.get_resolution().into_tuple(); + use gfx::{ + format::{Formatted, SurfaceTyped}, + memory::Typed, + }; + type WinSurfaceData = <::Surface as SurfaceTyped>::DataType; + let download = self + .factory + .create_download_buffer::(width as usize * height as usize)?; + self.encoder.copy_texture_to_buffer_raw( + self.win_color_view.raw().get_texture(), + None, + gfx::texture::RawImageInfo { + xoffset: 0, + yoffset: 0, + zoffset: 0, + width, + height, + depth: 0, + format: WinColorFmt::get_format(), + mipmap: 0, + }, + download.raw(), + 0, + )?; + self.flush(); - if let Err(err) = wgpu_profiler::chrometrace::write_chrometrace( - std::path::Path::new(&file_name), - &self.profile_times, - ) { - error!(?err, "Failed to save GPU timing snapshot"); + // Assumes that the format is Rgba8. + let raw_data = self + .factory + .read_mapping(&download)? + .chunks_exact(width as usize) + .rev() + .flatten() + .flatten() + .map(|&e| e) + .collect::>(); + Ok(image::DynamicImage::ImageRgba8( + // Should not fail if the dimensions are correct. + image::ImageBuffer::from_raw(width as u32, height as u32, raw_data).unwrap(), + )) + } + + /// Queue the rendering of the provided skybox model in the upcoming frame. + pub fn render_skybox( + &mut self, + model: &Model, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.skybox_pipeline.pso, + &skybox::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided figure model in the upcoming frame. + pub fn render_figure( + &mut self, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, + locals: &Consts, + bones: &Consts, + lod: &lod_terrain::LodData, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) } else { - info!("Saved GPU timing snapshot as: {}", file_name); - } - } + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.figure_pipeline.pso, + &figure::pipe::Data { + vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + bones: bones.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); } - // Consider reenabling at some time - // - // /// Queue the rendering of the player silhouette in the upcoming frame. - // pub fn render_player_shadow( - // &mut self, - // _model: &figure::FigureModel, - // _col_lights: &Texture, - // _global: &GlobalModel, - // _bones: &Consts, - // _lod: &lod_terrain::LodData, - // _locals: &Consts, - // ) { - // // FIXME: Consider reenabling at some point. - // /* let (point_shadow_maps, directed_shadow_maps) = - // if let Some(shadow_map) = &mut self.shadow_map { - // ( - // ( - // shadow_map.point_res.clone(), - // shadow_map.point_sampler.clone(), - // ), - // ( - // shadow_map.directed_res.clone(), - // shadow_map.directed_sampler.clone(), - // ), - // ) - // } else { - // ( - // (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), - // (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), - // ) - // }; - // let model = &model.opaque; + /// Queue the rendering of the player silhouette in the upcoming frame. + pub fn render_player_shadow( + &mut self, + _model: &figure::FigureModel, + _col_lights: &Texture, + _global: &GlobalModel, + _bones: &Consts, + _lod: &lod_terrain::LodData, + _locals: &Consts, + ) { + // FIXME: Consider reenabling at some point. + /* let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; - // self.encoder.draw( - // &gfx::Slice { - // start: model.vertex_range().start, - // end: model.vertex_range().end, - // base_vertex: 0, - // instances: None, - // buffer: gfx::IndexBuffer::Auto, - // }, - // &self.player_shadow_pipeline.pso, - // &figure::pipe::Data { - // vbuf: model.vbuf.clone(), - // col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), - // locals: locals.buf.clone(), - // globals: global.globals.buf.clone(), - // bones: bones.buf.clone(), - // lights: global.lights.buf.clone(), - // shadows: global.shadows.buf.clone(), - // light_shadows: global.shadow_mats.buf.clone(), - // point_shadow_maps, - // directed_shadow_maps, - // noise: (self.noise_tex.srv.clone(), - // self.noise_tex.sampler.clone()), alt: (lod.alt.srv.clone(), - // lod.alt.sampler.clone()), horizon: (lod.horizon.srv.clone(), - // lod.horizon.sampler.clone()), tgt_color: - // self.tgt_color_view.clone(), tgt_depth: - // (self.tgt_depth_view.clone()/* , (0, 0) */), }, - // ); */ - // } + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.player_shadow_pipeline.pso, + &figure::pipe::Data { + vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + bones: bones.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (0, 0) */), + }, + ); */ + } + + /// Queue the rendering of the player model in the upcoming frame. + pub fn render_player( + &mut self, + model: &figure::FigureModel, + col_lights: &Texture, + global: &GlobalModel, + locals: &Consts, + bones: &Consts, + lod: &lod_terrain::LodData, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + let model = &model.opaque; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.figure_pipeline.pso, + &figure::pipe::Data { + vbuf: model.vbuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + bones: bones.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided terrain chunk model in the upcoming + /// frame. + pub fn render_terrain_chunk( + &mut self, + model: &Model, + col_lights: &Texture, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.terrain_pipeline.pso, + &terrain::pipe::Data { + vbuf: model.vbuf.clone(), + // TODO: Consider splitting out texture atlas data into a separate vertex buffer, + // since we don't need it for things like global.shadows. + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of a shadow map from a point light in the upcoming + /// frame. + pub fn render_shadow_point( + &mut self, + model: &Model, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + + // let point_encoder = &mut shadow_map.point_encoder; + let point_encoder = &mut self.encoder; + point_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.point_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.point_depth_stencil_view.clone(), + }, + ); + } + + /// Queue the rendering of terrain shadow map from all directional lights in + /// the upcoming frame. + pub fn render_terrain_shadow_directed( + &mut self, + model: &Model, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.terrain_directed_pipeline.pso, + &shadow::pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: terrain_locals.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), + }, + ); + } + + /// Queue the rendering of figure shadow map from all directional lights in + /// the upcoming frame. + pub fn render_figure_shadow_directed( + &mut self, + model: &figure::FigureModel, + global: &GlobalModel, + figure_locals: &Consts, + bones: &Consts, + locals: &Consts, + ) { + if !self.mode.shadow.is_map() { + return; + } + // NOTE: Don't render shadows if the shader is not supported. + let shadow_map = if let Some(shadow_map) = &mut self.shadow_map { + shadow_map + } else { + return; + }; + let model = &model.opaque; + + // let directed_encoder = &mut shadow_map.directed_encoder; + let directed_encoder = &mut self.encoder; + directed_encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &shadow_map.figure_directed_pipeline.pso, + &shadow::figure_pipe::Data { + // Terrain vertex stuff + vbuf: model.vbuf.clone(), + locals: figure_locals.buf.clone(), + bones: bones.buf.clone(), + globals: global.globals.buf.clone(), + + // Shadow stuff + light_shadows: locals.buf.clone(), + tgt_depth_stencil: shadow_map.directed_depth_stencil_view.clone(), + }, + ); + } + + /// Queue the rendering of the provided terrain chunk model in the upcoming + /// frame. + pub fn render_fluid_chunk( + &mut self, + model: &Model, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + waves: &Texture, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.fluid_pipeline.pso, + &fluid::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + waves: (waves.srv.clone(), waves.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided terrain chunk model in the upcoming + /// frame. + pub fn render_sprites( + &mut self, + model: &Model, + col_lights: &Texture, + global: &GlobalModel, + terrain_locals: &Consts, + locals: &Consts, + instances: &Instances, + lod: &lod_terrain::LodData, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: Some((instances.count() as u32, 0)), + buffer: gfx::IndexBuffer::Auto, + }, + &self.sprite_pipeline.pso, + &sprite::pipe::Data { + vbuf: model.vbuf.clone(), + ibuf: instances.ibuf.clone(), + col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()), + terrain_locals: terrain_locals.buf.clone(), + // NOTE: It would be nice if this wasn't needed and we could use a constant buffer + // offset into the sprite data. Hopefully, when we switch to wgpu we can do this, + // as it offers the exact API we want (the equivalent can be done in OpenGL using + // glBindBufferOffset). + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided LoD terrain model in the upcoming + /// frame. + pub fn render_lod_terrain( + &mut self, + model: &Model, + global: &GlobalModel, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.lod_terrain_pipeline.pso, + &lod_terrain::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: global.globals.buf.clone(), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + map: (lod.map.srv.clone(), lod.map.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided particle in the upcoming frame. + pub fn render_particles( + &mut self, + model: &Model, + global: &GlobalModel, + instances: &Instances, + lod: &lod_terrain::LodData, + ) { + let (point_shadow_maps, directed_shadow_maps) = + if let Some(shadow_map) = &mut self.shadow_map { + ( + ( + shadow_map.point_res.clone(), + shadow_map.point_sampler.clone(), + ), + ( + shadow_map.directed_res.clone(), + shadow_map.directed_sampler.clone(), + ), + ) + } else { + ( + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + ) + }; + + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: Some((instances.count() as u32, 0)), + buffer: gfx::IndexBuffer::Auto, + }, + &self.particle_pipeline.pso, + &particle::pipe::Data { + vbuf: model.vbuf.clone(), + ibuf: instances.ibuf.clone(), + globals: global.globals.buf.clone(), + lights: global.lights.buf.clone(), + shadows: global.shadows.buf.clone(), + light_shadows: global.shadow_mats.buf.clone(), + point_shadow_maps, + directed_shadow_maps, + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + tgt_color: self.tgt_color_view.clone(), + tgt_depth_stencil: (self.tgt_depth_stencil_view.clone()/* , (1, 1) */), + }, + ); + } + + /// Queue the rendering of the provided UI element in the upcoming frame. + pub fn render_ui_element>( + &mut self, + model: Model, + tex: &Texture, + scissor: Aabr, + globals: &Consts, + locals: &Consts, + ) where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, + { + let Aabr { min, max } = scissor; + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range.start, + end: model.vertex_range.end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.ui_pipeline.pso, + &ui::pipe::Data { + vbuf: model.vbuf, + scissor: gfx::Rect { + x: min.x, + y: min.y, + w: max.x - min.x, + h: max.y - min.y, + }, + tex: (tex.srv.clone(), tex.sampler.clone()), + locals: locals.buf.clone(), + globals: globals.buf.clone(), + tgt_color: self.win_color_view.clone(), + tgt_depth: self.win_depth_view.clone(), + }, + ); + } + + pub fn render_clouds( + &mut self, + model: &Model, + globals: &Consts, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.clouds_pipeline.pso, + &clouds::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: globals.buf.clone(), + map: (lod.map.srv.clone(), lod.map.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + color_sampler: (self.tgt_color_res.clone(), self.sampler.clone()), + depth_sampler: (self.tgt_depth_res.clone(), self.sampler.clone()), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + tgt_color: self.tgt_color_view_pp.clone(), + }, + ) + } + + pub fn render_post_process( + &mut self, + model: &Model, + globals: &Consts, + locals: &Consts, + lod: &lod_terrain::LodData, + ) { + self.encoder.draw( + &gfx::Slice { + start: model.vertex_range().start, + end: model.vertex_range().end, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, + &self.postprocess_pipeline.pso, + &postprocess::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: globals.buf.clone(), + map: (lod.map.srv.clone(), lod.map.sampler.clone()), + alt: (lod.alt.srv.clone(), lod.alt.sampler.clone()), + horizon: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()), + color_sampler: (self.tgt_color_res_pp.clone(), self.sampler.clone()), + depth_sampler: (self.tgt_depth_res.clone(), self.sampler.clone()), + noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), + tgt_color: self.win_color_view.clone(), + }, + ) + } } -fn create_quad_index_buffer_u16(device: &wgpu::Device, vert_length: usize) -> Buffer { - assert!(vert_length <= u16::MAX as usize); - let indices = [0, 1, 2, 2, 1, 3] - .iter() - .cycle() - .copied() - .take(vert_length / 4 * 6) - .enumerate() - .map(|(i, b)| (i / 6 * 4 + b) as u16) - .collect::>(); - - Buffer::new(device, wgpu::BufferUsage::INDEX, &indices) +struct GfxPipeline { + pso: gfx::pso::PipelineState, } -fn create_quad_index_buffer_u32(device: &wgpu::Device, vert_length: usize) -> Buffer { - assert!(vert_length <= u32::MAX as usize); - let indices = [0, 1, 2, 2, 1, 3] - .iter() - .cycle() - .copied() - .take(vert_length / 4 * 6) - .enumerate() - .map(|(i, b)| (i / 6 * 4 + b) as u32) - .collect::>(); +/// Creates all the pipelines used to render. +#[allow(clippy::type_complexity)] // TODO: Pending review in #587 +fn create_pipelines( + factory: &mut gfx_backend::Factory, + shaders: &Shaders, + mode: &RenderMode, + has_shadow_views: bool, +) -> Result< + ( + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + GfxPipeline>, + Option>>, + Option>>, + Option>>, + ), + RenderError, +> { + // We dynamically add extra configuration settings to the constants file. + let constants = format!( + r#" +{} - Buffer::new(device, wgpu::BufferUsage::INDEX, &indices) +#define VOXYGEN_COMPUTATION_PREFERENCE {} +#define FLUID_MODE {} +#define CLOUD_MODE {} +#define LIGHTING_ALGORITHM {} +#define SHADOW_MODE {} + +"#, + shaders.constants.read().0, + // TODO: Configurable vertex/fragment shader preference. + "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT", + match mode.fluid { + FluidMode::Cheap => "FLUID_MODE_CHEAP", + FluidMode::Shiny => "FLUID_MODE_SHINY", + }, + match mode.cloud { + CloudMode::None => "CLOUD_MODE_NONE", + CloudMode::Minimal => "CLOUD_MODE_MINIMAL", + CloudMode::Low => "CLOUD_MODE_LOW", + CloudMode::Medium => "CLOUD_MODE_MEDIUM", + CloudMode::High => "CLOUD_MODE_HIGH", + CloudMode::Ultra => "CLOUD_MODE_ULTRA", + }, + match mode.lighting { + LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN", + LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", + LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN", + }, + match mode.shadow { + ShadowMode::None => "SHADOW_MODE_NONE", + ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP", + ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP", + }, + ); + + let anti_alias = &match mode.aa { + AaMode::None => shaders.anti_alias_none, + AaMode::Fxaa => shaders.anti_alias_fxaa, + AaMode::MsaaX4 => shaders.anti_alias_msaa_x4, + AaMode::MsaaX8 => shaders.anti_alias_msaa_x8, + AaMode::MsaaX16 => shaders.anti_alias_msaa_x16, + }; + + let cloud = &match mode.cloud { + CloudMode::None => shaders.cloud_none, + _ => shaders.cloud_regular, + }; + + let mut include_ctx = IncludeContext::new(); + include_ctx.include("constants.glsl", &constants); + include_ctx.include("globals.glsl", &shaders.globals.read().0); + include_ctx.include("shadows.glsl", &shaders.shadows.read().0); + include_ctx.include("sky.glsl", &shaders.sky.read().0); + include_ctx.include("light.glsl", &shaders.light.read().0); + include_ctx.include("srgb.glsl", &shaders.srgb.read().0); + include_ctx.include("random.glsl", &shaders.random.read().0); + include_ctx.include("lod.glsl", &shaders.lod.read().0); + include_ctx.include("anti-aliasing.glsl", &anti_alias.read().0); + include_ctx.include("cloud.glsl", &cloud.read().0); + + // Construct a pipeline for rendering skyboxes + let skybox_pipeline = create_pipeline( + factory, + skybox::pipe::new(), + &shaders.skybox_vert.read().0, + &shaders.skybox_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering figures + let figure_pipeline = create_pipeline( + factory, + figure::pipe::new(), + &shaders.figure_vert.read().0, + &shaders.figure_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering terrain + let terrain_pipeline = create_pipeline( + factory, + terrain::pipe::new(), + &shaders.terrain_vert.read().0, + &shaders.terrain_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering fluids + let fluid_pipeline = create_pipeline( + factory, + fluid::pipe::new(), + &shaders.fluid_vert.read().0, + &match mode.fluid { + FluidMode::Cheap => shaders.fluid_frag_cheap, + FluidMode::Shiny => shaders.fluid_frag_shiny, + } + .read() + .0, + &include_ctx, + gfx::state::CullFace::Nothing, + )?; + + // Construct a pipeline for rendering sprites + let sprite_pipeline = create_pipeline( + factory, + sprite::pipe::new(), + &shaders.sprite_vert.read().0, + &shaders.sprite_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering particles + let particle_pipeline = create_pipeline( + factory, + particle::pipe::new(), + &shaders.particle_vert.read().0, + &shaders.particle_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering UI elements + let ui_pipeline = create_pipeline( + factory, + ui::pipe::new(), + &shaders.ui_vert.read().0, + &shaders.ui_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering terrain + let lod_terrain_pipeline = create_pipeline( + factory, + lod_terrain::pipe::new(), + &shaders.lod_terrain_vert.read().0, + &shaders.lod_terrain_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering our clouds (a kind of post-processing) + let clouds_pipeline = create_pipeline( + factory, + clouds::pipe::new(), + &shaders.clouds_vert.read().0, + &shaders.clouds_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering our post-processing + let postprocess_pipeline = create_pipeline( + factory, + postprocess::pipe::new(), + &shaders.postprocess_vert.read().0, + &shaders.postprocess_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering the player silhouette + let player_shadow_pipeline = create_pipeline( + factory, + figure::pipe::Init { + tgt_depth_stencil: (gfx::preset::depth::PASS_TEST/*, + Stencil::new( + Comparison::Equal, + 0xff, + (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), + ),*/), + ..figure::pipe::new() + }, + &shaders.figure_vert.read().0, + &shaders.player_shadow_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + )?; + + // Construct a pipeline for rendering point light terrain shadow maps. + let point_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &shaders.terrain_point_shadow_vert.read().0, + Some(&shaders.light_shadows_geom.read().0), + &shaders.light_shadows_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 0)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!("Could not load point shadow map pipeline: {:?}", err); + None + }, + }; + + // Construct a pipeline for rendering directional light terrain shadow maps. + let terrain_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::pipe::new(), + &shaders.terrain_directed_shadow_vert.read().0, + None, + &shaders.directed_shadow_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!( + "Could not load directed terrain shadow map pipeline: {:?}", + err + ); + None + }, + }; + + // Construct a pipeline for rendering directional light figure shadow maps. + let figure_directed_shadow_pipeline = match create_shadow_pipeline( + factory, + shadow::figure_pipe::new(), + &shaders.figure_directed_shadow_vert.read().0, + None, + &shaders.directed_shadow_frag.read().0, + &include_ctx, + gfx::state::CullFace::Back, + None, // Some(gfx::state::Offset(2, 1)) + ) { + Ok(pipe) => Some(pipe), + Err(err) => { + warn!( + "Could not load directed figure shadow map pipeline: {:?}", + err + ); + None + }, + }; + + Ok(( + skybox_pipeline, + figure_pipeline, + terrain_pipeline, + fluid_pipeline, + sprite_pipeline, + particle_pipeline, + ui_pipeline, + lod_terrain_pipeline, + clouds_pipeline, + postprocess_pipeline, + player_shadow_pipeline, + point_shadow_pipeline, + terrain_directed_shadow_pipeline, + figure_directed_shadow_pipeline, + )) +} + +/// Create a new pipeline from the provided vertex shader and fragment shader. +fn create_pipeline( + factory: &mut gfx_backend::Factory, + pipe: P, + vs: &str, + fs: &str, + ctx: &IncludeContext, + cull_face: gfx::state::CullFace, +) -> Result, RenderError> { + let vs = ctx.expand(vs)?; + let fs = ctx.expand(fs)?; + + let program = factory.link_program(vs.as_bytes(), fs.as_bytes())?; + + let result = Ok(GfxPipeline { + pso: factory.create_pipeline_from_program( + &program, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + front_face: gfx::state::FrontFace::CounterClockwise, + cull_face, + method: gfx::state::RasterMethod::Fill, + offset: None, + samples: Some(gfx::state::MultiSample), + }, + pipe, + )?, + }); + + result +} + +/// Create a new shadow map pipeline. +fn create_shadow_pipeline( + factory: &mut gfx_backend::Factory, + pipe: P, + vs: &str, + gs: Option<&str>, + fs: &str, + ctx: &IncludeContext, + cull_face: gfx::state::CullFace, + offset: Option, +) -> Result, RenderError> { + let vs = ctx.expand(vs)?; + let gs = gs.map(|gs| ctx.expand(gs)).transpose()?; + let fs = ctx.expand(fs)?; + + let shader_set = if let Some(gs) = gs { + factory.create_shader_set_geometry(vs.as_bytes(), gs.as_bytes(), fs.as_bytes())? + } else { + factory.create_shader_set(vs.as_bytes(), fs.as_bytes())? + }; + + Ok(GfxPipeline { + pso: factory.create_pipeline_state( + &shader_set, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + front_face: gfx::state::FrontFace::CounterClockwise, + // Second-depth shadow mapping: should help reduce z-fighting provided all objects + // are "watertight" (every triangle edge is shared with at most one other + // triangle); this *should* be true for Veloren. + cull_face: match cull_face { + gfx::state::CullFace::Front => gfx::state::CullFace::Back, + gfx::state::CullFace::Back => gfx::state::CullFace::Front, + gfx::state::CullFace::Nothing => gfx::state::CullFace::Nothing, + }, + method: gfx::state::RasterMethod::Fill, + offset, + samples: None, + }, + pipe, + )?, + }) } diff --git a/voxygen/src/render/renderer/binding.rs b/voxygen/src/render/renderer/binding.rs deleted file mode 100644 index f42808c0a8..0000000000 --- a/voxygen/src/render/renderer/binding.rs +++ /dev/null @@ -1,88 +0,0 @@ -use super::{ - super::{ - pipelines::{ - debug, figure, lod_terrain, shadow, sprite, terrain, ui, ColLights, GlobalModel, - GlobalsBindGroup, - }, - texture::Texture, - }, - Renderer, -}; - -impl Renderer { - pub fn bind_globals( - &self, - global_model: &GlobalModel, - lod_data: &lod_terrain::LodData, - ) -> GlobalsBindGroup { - self.layouts - .global - .bind(&self.device, global_model, lod_data, &self.noise_tex) - } - - pub fn bind_sprite_globals( - &self, - global_model: &GlobalModel, - lod_data: &lod_terrain::LodData, - sprite_verts: &sprite::SpriteVerts, - ) -> sprite::SpriteGlobalsBindGroup { - self.layouts.sprite.bind_globals( - &self.device, - global_model, - lod_data, - &self.noise_tex, - sprite_verts, - ) - } - - pub fn create_debug_bound_locals(&mut self, vals: &[debug::Locals]) -> debug::BoundLocals { - let locals = self.create_consts(vals); - self.layouts.debug.bind_locals(&self.device, locals) - } - - pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals { - let locals = self.create_consts(vals); - self.layouts.ui.bind_locals(&self.device, locals) - } - - pub fn ui_bind_texture(&self, texture: &Texture) -> ui::TextureBindGroup { - self.layouts.ui.bind_texture(&self.device, texture) - } - - pub fn create_figure_bound_locals( - &mut self, - locals: &[figure::Locals], - bone_data: &[figure::BoneData], - ) -> figure::BoundLocals { - let locals = self.create_consts(locals); - let bone_data = self.create_consts(bone_data); - self.layouts - .figure - .bind_locals(&self.device, locals, bone_data) - } - - pub fn create_terrain_bound_locals( - &mut self, - locals: &[terrain::Locals], - ) -> terrain::BoundLocals { - let locals = self.create_consts(locals); - self.layouts.terrain.bind_locals(&self.device, locals) - } - - pub fn create_shadow_bound_locals(&mut self, locals: &[shadow::Locals]) -> shadow::BoundLocals { - let locals = self.create_consts(locals); - self.layouts.shadow.bind_locals(&self.device, locals) - } - - pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights { - self.layouts.global.bind_col_light(&self.device, col_light) - } - - pub fn terrain_bind_col_light(&self, col_light: Texture) -> ColLights { - self.layouts.global.bind_col_light(&self.device, col_light) - } - - pub fn sprite_bind_col_light(&self, col_light: Texture) -> ColLights { - self.layouts.global.bind_col_light(&self.device, col_light) - } -} diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs deleted file mode 100644 index 7e25c349d2..0000000000 --- a/voxygen/src/render/renderer/drawer.rs +++ /dev/null @@ -1,869 +0,0 @@ -use super::{ - super::{ - buffer::Buffer, - instances::Instances, - model::{DynamicModel, Model, SubModel}, - pipelines::{ - blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, - terrain, ui, ColLights, GlobalsBindGroup, - }, - }, - Renderer, ShadowMap, ShadowMapRenderer, -}; -use core::{num::NonZeroU32, ops::Range}; -use std::sync::Arc; -use vek::Aabr; -use wgpu_profiler::scope::{ManualOwningScope, OwningScope, Scope}; - -// Currently available pipelines -enum Pipelines<'frame> { - Interface(&'frame super::InterfacePipelines), - All(&'frame super::Pipelines), - // Should never be in this state for now but we need this to accound for super::State::Nothing - None, -} - -impl<'frame> Pipelines<'frame> { - fn ui(&self) -> Option<&ui::UiPipeline> { - match self { - Pipelines::Interface(pipelines) => Some(&pipelines.ui), - Pipelines::All(pipelines) => Some(&pipelines.ui), - Pipelines::None => None, - } - } - - fn blit(&self) -> Option<&blit::BlitPipeline> { - match self { - Pipelines::Interface(pipelines) => Some(&pipelines.blit), - Pipelines::All(pipelines) => Some(&pipelines.blit), - Pipelines::None => None, - } - } - - fn all(&self) -> Option<&super::Pipelines> { - match self { - Pipelines::All(pipelines) => Some(pipelines), - Pipelines::Interface(_) | Pipelines::None => None, - } - } -} - -// Borrow the fields we need from the renderer so that the GpuProfiler can be -// disjointly borrowed mutably -struct RendererBorrow<'frame> { - queue: &'frame wgpu::Queue, - device: &'frame wgpu::Device, - shadow: Option<&'frame super::Shadow>, - pipelines: Pipelines<'frame>, - locals: &'frame super::locals::Locals, - views: &'frame super::Views, - mode: &'frame super::super::RenderMode, - quad_index_buffer_u16: &'frame Buffer, - quad_index_buffer_u32: &'frame Buffer, -} - -pub struct Drawer<'frame> { - encoder: Option>, - borrow: RendererBorrow<'frame>, - swap_tex: wgpu::SwapChainTexture, - globals: &'frame GlobalsBindGroup, - // Texture and other info for taking a screenshot - // Writes to this instead in the third pass if it is present - taking_screenshot: Option, -} - -impl<'frame> Drawer<'frame> { - pub fn new( - encoder: wgpu::CommandEncoder, - renderer: &'frame mut Renderer, - swap_tex: wgpu::SwapChainTexture, - globals: &'frame GlobalsBindGroup, - ) -> Self { - let taking_screenshot = renderer.take_screenshot.take().map(|screenshot_fn| { - super::screenshot::TakeScreenshot::new( - &renderer.device, - &renderer.layouts.blit, - &renderer.sampler, - &renderer.sc_desc, - screenshot_fn, - ) - }); - - let (pipelines, shadow) = match &renderer.state { - super::State::Interface { pipelines, .. } => (Pipelines::Interface(pipelines), None), - super::State::Complete { - pipelines, shadow, .. - } => (Pipelines::All(pipelines), Some(shadow)), - super::State::Nothing => (Pipelines::None, None), - }; - - let borrow = RendererBorrow { - queue: &renderer.queue, - device: &renderer.device, - shadow, - pipelines, - locals: &renderer.locals, - views: &renderer.views, - mode: &renderer.mode, - quad_index_buffer_u16: &renderer.quad_index_buffer_u16, - quad_index_buffer_u32: &renderer.quad_index_buffer_u32, - }; - - let encoder = - ManualOwningScope::start("frame", &mut renderer.profiler, encoder, borrow.device); - - Self { - encoder: Some(encoder), - borrow, - swap_tex, - globals, - taking_screenshot, - } - } - - /// Get the render mode. - pub fn render_mode(&self) -> &super::super::RenderMode { self.borrow.mode } - - /// Returns None if the shadow renderer is not enabled at some level or the - /// pipelines are not available yet - pub fn shadow_pass(&mut self) -> Option { - if !self.borrow.mode.shadow.is_map() { - return None; - } - - if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow?.map { - let encoder = self.encoder.as_mut().unwrap(); - let device = self.borrow.device; - let mut render_pass = - encoder.scoped_render_pass("shadow_pass", device, &wgpu::RenderPassDescriptor { - label: Some("shadow pass"), - color_attachments: &[], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &shadow_renderer.directed_depth.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - - render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - - Some(ShadowPassDrawer { - render_pass, - borrow: &self.borrow, - shadow_renderer, - }) - } else { - None - } - } - - /// Returns None if all the pipelines are not available - pub fn first_pass(&mut self) -> Option { - let pipelines = self.borrow.pipelines.all()?; - // Note: this becomes Some once pipeline creation is complete even if shadows - // are not enabled - let shadow = self.borrow.shadow?; - - let encoder = self.encoder.as_mut().unwrap(); - let device = self.borrow.device; - let mut render_pass = - encoder.scoped_render_pass("first_pass", device, &wgpu::RenderPassDescriptor { - label: Some("first pass"), - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &self.borrow.views.tgt_color, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: true, - }, - }], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.borrow.views.tgt_depth, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(0.0), - store: true, - }), - stencil_ops: None, - }), - }); - - render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]); - - Some(FirstPassDrawer { - render_pass, - borrow: &self.borrow, - pipelines, - globals: self.globals, - }) - } - - /// Returns None if the clouds pipeline is not available - pub fn second_pass(&mut self) -> Option { - let pipeline = &self.borrow.pipelines.all()?.clouds; - - let encoder = self.encoder.as_mut().unwrap(); - let device = self.borrow.device; - let mut render_pass = - encoder.scoped_render_pass("second_pass", device, &wgpu::RenderPassDescriptor { - label: Some("second pass (clouds)"), - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &self.borrow.views.tgt_color_pp, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - - render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - - Some(SecondPassDrawer { - render_pass, - borrow: &self.borrow, - pipeline, - }) - } - - pub fn third_pass(&mut self) -> ThirdPassDrawer { - let encoder = self.encoder.as_mut().unwrap(); - let device = self.borrow.device; - let mut render_pass = - encoder.scoped_render_pass("third_pass", device, &wgpu::RenderPassDescriptor { - label: Some("third pass (postprocess + ui)"), - color_attachments: &[wgpu::RenderPassColorAttachment { - // If a screenshot was requested render to that as an intermediate texture - // instead - view: self - .taking_screenshot - .as_ref() - .map_or(&self.swap_tex.view, |s| s.texture_view()), - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - - render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - - ThirdPassDrawer { - render_pass, - borrow: &self.borrow, - } - } - - /// Does nothing if the shadow pipelines are not available or shadow map - /// rendering is disabled - pub fn draw_point_shadows<'data: 'frame>( - &mut self, - matrices: &[shadow::PointLightMatrix; 126], - chunks: impl Clone - + Iterator, &'data terrain::BoundLocals)>, - ) { - if !self.borrow.mode.shadow.is_map() { - return; - } - - if let Some(ShadowMap::Enabled(ref shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) { - let device = self.borrow.device; - let mut encoder = self - .encoder - .as_mut() - .unwrap() - .scope("point shadows", device); - const STRIDE: usize = std::mem::size_of::(); - let data = bytemuck::cast_slice(matrices); - - for face in 0..6 { - // TODO: view creation cost? - let view = - shadow_renderer - .point_depth - .tex - .create_view(&wgpu::TextureViewDescriptor { - label: Some("Point shadow cubemap face"), - format: None, - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: face, - array_layer_count: NonZeroU32::new(1), - }); - - let label = format!("point shadow face-{} pass", face); - let mut render_pass = - encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor { - label: Some(&label), - color_attachments: &[], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - - render_pass.set_pipeline(&shadow_renderer.point_pipeline.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - - (0../*20*/1).for_each(|point_light| { - render_pass.set_push_constants( - wgpu::ShaderStage::all(), - 0, - &data[(6 * (point_light + 1) * STRIDE + face as usize * STRIDE) - ..(6 * (point_light + 1) * STRIDE + (face + 1) as usize * STRIDE)], - ); - chunks.clone().for_each(|(model, locals)| { - render_pass.set_bind_group(1, &locals.bind_group, &[]); - render_pass.set_vertex_buffer(0, model.buf().slice(..)); - render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - }); - }); - } - } - } - - /// Clear all the shadow textures, useful if directed shadows (shadow_pass) - /// and point light shadows (draw_point_shadows) are unused and thus the - /// textures will otherwise not be cleared after either their - /// initialization or their last use - /// NOTE: could simply use the above passes except `draw_point_shadows` - /// requires an array of matrices that could be a pain to construct - /// simply for clearing - /// - /// Does nothing if the shadow pipelines are not available (although they - /// aren't used here they are needed for the ShadowMap to exist) - pub fn clear_shadows(&mut self) { - if let Some(ShadowMap::Enabled(ref shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) { - let device = self.borrow.device; - let encoder = self.encoder.as_mut().unwrap(); - let _ = encoder.scoped_render_pass( - "clear_directed_shadow", - device, - &wgpu::RenderPassDescriptor { - label: Some("clear directed shadow pass"), - color_attachments: &[], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &shadow_renderer.directed_depth.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }, - ); - - for face in 0..6 { - // TODO: view creation cost? - let view = - shadow_renderer - .point_depth - .tex - .create_view(&wgpu::TextureViewDescriptor { - label: Some("Point shadow cubemap face"), - format: None, - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: face, - array_layer_count: NonZeroU32::new(1), - }); - - let label = format!("clear point shadow face-{} pass", face); - let _ = encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor { - label: Some(&label), - color_attachments: &[], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - } - } - } -} - -impl<'frame> Drop for Drawer<'frame> { - fn drop(&mut self) { - let mut encoder = self.encoder.take().unwrap(); - - // If taking a screenshot and the blit pipeline is available - // NOTE: blit pipeline should always be available for now so we don't report an - // error if it isn't - if let Some((screenshot, blit)) = self - .taking_screenshot - .take() - .zip(self.borrow.pipelines.blit()) - { - // Image needs to be copied from the screenshot texture to the swapchain texture - let mut render_pass = encoder.scoped_render_pass( - "screenshot blit", - self.borrow.device, - &wgpu::RenderPassDescriptor { - label: Some("Blit screenshot pass"), - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &self.swap_tex.view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: true, - }, - }], - depth_stencil_attachment: None, - }, - ); - render_pass.set_pipeline(&blit.pipeline); - render_pass.set_bind_group(0, &screenshot.bind_group(), &[]); - render_pass.draw(0..3, 0..1); - drop(render_pass); - // Issues a command to copy from the texture to a buffer and then sends the - // buffer off to another thread to be mapped and processed - screenshot.download_and_handle(&mut encoder); - } - - let (mut encoder, profiler) = encoder.end_scope(); - profiler.resolve_queries(&mut encoder); - - // It is recommended to only do one submit per frame - self.borrow.queue.submit(std::iter::once(encoder.finish())); - - profiler - .end_frame() - .expect("Gpu profiler error! Maybe there was an unclosed scope?"); - } -} - -// Shadow pass -pub struct ShadowPassDrawer<'pass> { - render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>, - borrow: &'pass RendererBorrow<'pass>, - shadow_renderer: &'pass ShadowMapRenderer, -} - -impl<'pass> ShadowPassDrawer<'pass> { - pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> { - let mut render_pass = self - .render_pass - .scope("direcred_figure_shadows", self.borrow.device); - - render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - FigureShadowDrawer { render_pass } - } - - pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> { - let mut render_pass = self - .render_pass - .scope("direcred_terrain_shadows", self.borrow.device); - - render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - TerrainShadowDrawer { render_pass } - } -} - -pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: SubModel<'data, terrain::Vertex>, - locals: &'data figure::BoundLocals, - ) { - self.render_pass.set_bind_group(1, &locals.bind_group, &[]); - self.render_pass.set_vertex_buffer(0, model.buf()); - self.render_pass - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } -} - -pub struct TerrainShadowDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: &'data Model, - locals: &'data terrain::BoundLocals, - ) { - self.render_pass.set_bind_group(1, &locals.bind_group, &[]); - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); - self.render_pass - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } -} - -// First pass -pub struct FirstPassDrawer<'pass> { - pub(super) render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>, - borrow: &'pass RendererBorrow<'pass>, - pipelines: &'pass super::Pipelines, - globals: &'pass GlobalsBindGroup, -} - -impl<'pass> FirstPassDrawer<'pass> { - pub fn draw_skybox<'data: 'pass>(&mut self, model: &'data Model) { - let mut render_pass = self.render_pass.scope("skybox", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.skybox.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - render_pass.set_vertex_buffer(0, model.buf().slice(..)); - render_pass.draw(0..model.len() as u32, 0..1); - } - - pub fn draw_debug(&mut self) -> DebugDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("debug", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.debug.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - DebugDrawer { render_pass } - } - - pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model) { - let mut render_pass = self.render_pass.scope("lod_terrain", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.lod_terrain.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - render_pass.set_vertex_buffer(0, model.buf().slice(..)); - render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } - - pub fn draw_figures(&mut self) -> FigureDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("figures", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.figure.pipeline); - // Note: figures use the same vertex type as the terrain - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - FigureDrawer { render_pass } - } - - pub fn draw_terrain(&mut self) -> TerrainDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("terrain", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.terrain.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - TerrainDrawer { - render_pass, - col_lights: None, - } - } - - pub fn draw_particles(&mut self) -> ParticleDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("particles", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.particle.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - ParticleDrawer { render_pass } - } - - pub fn draw_sprites<'data: 'pass>( - &mut self, - globals: &'data sprite::SpriteGlobalsBindGroup, - col_lights: &'data ColLights, - ) -> SpriteDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("sprites", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.sprite.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - render_pass.set_bind_group(0, &globals.bind_group, &[]); - render_pass.set_bind_group(3, &col_lights.bind_group, &[]); - - SpriteDrawer { - render_pass, - globals: self.globals, - } - } - - pub fn draw_fluid(&mut self) -> FluidDrawer<'_, 'pass> { - let mut render_pass = self.render_pass.scope("fluid", self.borrow.device); - - render_pass.set_pipeline(&self.pipelines.fluid.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - FluidDrawer { render_pass } - } -} - -pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: &'data Model, - locals: &'data debug::BoundLocals, - ) { - self.render_pass.set_bind_group(1, &locals.bind_group, &[]); - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); - self.render_pass.draw(0..model.len() as u32, 0..1); - } -} - -pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: SubModel<'data, terrain::Vertex>, - locals: &'data figure::BoundLocals, - // TODO: don't rebind this every time once they are shared between figures - col_lights: &'data ColLights, - ) { - self.render_pass.set_bind_group(2, &locals.bind_group, &[]); - self.render_pass - .set_bind_group(3, &col_lights.bind_group, &[]); - self.render_pass.set_vertex_buffer(0, model.buf()); - self.render_pass - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } -} - -pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, - col_lights: Option<&'pass_ref Arc>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: &'data Model, - col_lights: &'data Arc>, - locals: &'data terrain::BoundLocals, - ) { - if self.col_lights - // Check if we are still using the same atlas texture as the previous drawn - // chunk - .filter(|current_col_lights| Arc::ptr_eq(current_col_lights, col_lights)) - .is_none() - { - self.render_pass - .set_bind_group(3, &col_lights.bind_group, &[]); // TODO: put this in slot 2 - self.col_lights = Some(col_lights); - }; - - self.render_pass.set_bind_group(2, &locals.bind_group, &[]); // TODO: put this in slot 3 - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); - self.render_pass - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } -} - -pub struct ParticleDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> { - // Note: if we ever need to draw less than the whole model, these APIs can be - // changed - pub fn draw<'data: 'pass>( - &mut self, - model: &'data Model, - instances: &'data Instances, - ) { - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); - self.render_pass - .set_vertex_buffer(1, instances.buf().slice(..)); - self.render_pass - // TODO: since we cast to u32 maybe this should returned by the len/count functions? - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..instances.count() as u32); - } -} - -pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, - globals: &'pass GlobalsBindGroup, -} - -impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - terrain_locals: &'data terrain::BoundLocals, - instances: &'data Instances, - ) { - self.render_pass - .set_bind_group(2, &terrain_locals.bind_group, &[]); - - self.render_pass - .set_vertex_buffer(0, instances.buf().slice(..)); - self.render_pass.draw_indexed( - 0..sprite::VERT_PAGE_SIZE / 4 * 6, - 0, - 0..instances.count() as u32, - ); - } -} - -impl<'pass_ref, 'pass: 'pass_ref> Drop for SpriteDrawer<'pass_ref, 'pass> { - fn drop(&mut self) { - // Reset to regular globals - self.render_pass - .set_bind_group(0, &self.globals.bind_group, &[]); - } -} - -pub struct FluidDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> { - pub fn draw<'data: 'pass>( - &mut self, - model: &'data Model, - locals: &'data terrain::BoundLocals, - ) { - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)); - self.render_pass.set_bind_group(2, &locals.bind_group, &[]); - self.render_pass - .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1); - } -} - -// Second pass: clouds -pub struct SecondPassDrawer<'pass> { - render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>, - borrow: &'pass RendererBorrow<'pass>, - pipeline: &'pass clouds::CloudsPipeline, -} - -impl<'pass> SecondPassDrawer<'pass> { - pub fn draw_clouds(&mut self) { - self.render_pass.set_pipeline(&self.pipeline.pipeline); - self.render_pass - .set_bind_group(1, &self.borrow.locals.clouds_bind.bind_group, &[]); - self.render_pass.draw(0..3, 0..1); - } -} - -/// Third pass: postprocess + ui -pub struct ThirdPassDrawer<'pass> { - render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>, - borrow: &'pass RendererBorrow<'pass>, -} - -impl<'pass> ThirdPassDrawer<'pass> { - /// Does nothing if the postprocess pipeline is not available - pub fn draw_postprocess(&mut self) { - let postprocess = match self.borrow.pipelines.all() { - Some(p) => &p.postprocess, - None => return, - }; - - let mut render_pass = self.render_pass.scope("postprocess", self.borrow.device); - render_pass.set_pipeline(&postprocess.pipeline); - render_pass.set_bind_group(1, &self.borrow.locals.postprocess_bind.bind_group, &[]); - render_pass.draw(0..3, 0..1); - } - - /// Returns None if the UI pipeline is not available (note: this should - /// never be the case for now) - pub fn draw_ui(&mut self) -> Option> { - let ui = self.borrow.pipelines.ui()?; - - let mut render_pass = self.render_pass.scope("ui", self.borrow.device); - render_pass.set_pipeline(&ui.pipeline); - set_quad_index_buffer::(&mut render_pass, &self.borrow); - - Some(UiDrawer { render_pass }) - } -} - -pub struct UiDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, -} - -pub struct PreparedUiDrawer<'pass_ref, 'pass: 'pass_ref> { - render_pass: &'pass_ref mut wgpu::RenderPass<'pass>, -} - -impl<'pass_ref, 'pass: 'pass_ref> UiDrawer<'pass_ref, 'pass> { - /// Set vertex buffer, initial scissor, and locals - /// These can be changed later but this ensures that they don't have to be - /// set with every draw call - pub fn prepare<'data: 'pass>( - &mut self, - locals: &'data ui::BoundLocals, - buf: &'data DynamicModel, - scissor: Aabr, - ) -> PreparedUiDrawer<'_, 'pass> { - // Note: not actually prepared yet - // we do this to avoid having to write extra code for the set functions - let mut prepared = PreparedUiDrawer { - render_pass: &mut *self.render_pass, - }; - // Prepare - prepared.set_locals(locals); - prepared.set_model(buf); - prepared.set_scissor(scissor); - - prepared - } -} - -impl<'pass_ref, 'pass: 'pass_ref> PreparedUiDrawer<'pass_ref, 'pass> { - pub fn set_locals<'data: 'pass>(&mut self, locals: &'data ui::BoundLocals) { - self.render_pass.set_bind_group(1, &locals.bind_group, &[]); - } - - pub fn set_model<'data: 'pass>(&mut self, model: &'data DynamicModel) { - self.render_pass.set_vertex_buffer(0, model.buf().slice(..)) - } - - pub fn set_scissor(&mut self, scissor: Aabr) { - let Aabr { min, max } = scissor; - self.render_pass.set_scissor_rect( - min.x as u32, - min.y as u32, - (max.x - min.x) as u32, - (max.y - min.y) as u32, - ); - } - - pub fn draw<'data: 'pass>(&mut self, texture: &'data ui::TextureBindGroup, verts: Range) { - self.render_pass.set_bind_group(2, &texture.bind_group, &[]); - self.render_pass.draw(verts, 0..1); - } -} - -fn set_quad_index_buffer<'a, V: super::super::Vertex>( - pass: &mut wgpu::RenderPass<'a>, - borrow: &RendererBorrow<'a>, -) { - if let Some(format) = V::QUADS_INDEX { - let slice = match format { - wgpu::IndexFormat::Uint16 => borrow.quad_index_buffer_u16.buf.slice(..), - wgpu::IndexFormat::Uint32 => borrow.quad_index_buffer_u32.buf.slice(..), - }; - - pass.set_index_buffer(slice, format); - } -} diff --git a/voxygen/src/render/renderer/locals.rs b/voxygen/src/render/renderer/locals.rs deleted file mode 100644 index b7c94c5396..0000000000 --- a/voxygen/src/render/renderer/locals.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::{ - super::{ - consts::Consts, - pipelines::{clouds, postprocess}, - }, - Layouts, -}; - -pub struct Locals { - pub clouds: Consts, - pub clouds_bind: clouds::BindGroup, - - pub postprocess: Consts, - pub postprocess_bind: postprocess::BindGroup, -} - -impl Locals { - pub(super) fn new( - device: &wgpu::Device, - layouts: &Layouts, - clouds_locals: Consts, - postprocess_locals: Consts, - tgt_color_view: &wgpu::TextureView, - tgt_depth_view: &wgpu::TextureView, - tgt_color_pp_view: &wgpu::TextureView, - sampler: &wgpu::Sampler, - depth_sampler: &wgpu::Sampler, - ) -> Self { - let clouds_bind = layouts.clouds.bind( - device, - tgt_color_view, - tgt_depth_view, - sampler, - depth_sampler, - &clouds_locals, - ); - let postprocess_bind = - layouts - .postprocess - .bind(device, tgt_color_pp_view, sampler, &postprocess_locals); - - Self { - clouds: clouds_locals, - clouds_bind, - postprocess: postprocess_locals, - postprocess_bind, - } - } - - pub(super) fn rebind( - &mut self, - device: &wgpu::Device, - layouts: &Layouts, - // Call when these are recreated and need to be rebound - // e.g. resizing - tgt_color_view: &wgpu::TextureView, - tgt_depth_view: &wgpu::TextureView, - tgt_color_pp_view: &wgpu::TextureView, - sampler: &wgpu::Sampler, - depth_sampler: &wgpu::Sampler, - ) { - self.clouds_bind = layouts.clouds.bind( - device, - tgt_color_view, - tgt_depth_view, - sampler, - depth_sampler, - &self.clouds, - ); - self.postprocess_bind = - layouts - .postprocess - .bind(device, tgt_color_pp_view, sampler, &self.postprocess); - } -} diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs deleted file mode 100644 index 6ad5e483a9..0000000000 --- a/voxygen/src/render/renderer/pipeline_creation.rs +++ /dev/null @@ -1,914 +0,0 @@ -use super::{ - super::{ - pipelines::{ - blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, - sprite, terrain, ui, - }, - AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode, - }, - shaders::Shaders, - Layouts, -}; -use common_base::prof_span; -use std::sync::Arc; - -/// All the pipelines -pub struct Pipelines { - pub debug: debug::DebugPipeline, - pub figure: figure::FigurePipeline, - pub fluid: fluid::FluidPipeline, - pub lod_terrain: lod_terrain::LodTerrainPipeline, - pub particle: particle::ParticlePipeline, - pub clouds: clouds::CloudsPipeline, - pub postprocess: postprocess::PostProcessPipeline, - // Consider reenabling at some time - // player_shadow: figure::FigurePipeline, - pub skybox: skybox::SkyboxPipeline, - pub sprite: sprite::SpritePipeline, - pub terrain: terrain::TerrainPipeline, - pub ui: ui::UiPipeline, - pub blit: blit::BlitPipeline, -} - -/// Pipelines that are needed to render 3D stuff in-game -/// Use to decouple interface pipeline creation when initializing the renderer -pub struct IngamePipelines { - debug: debug::DebugPipeline, - figure: figure::FigurePipeline, - fluid: fluid::FluidPipeline, - lod_terrain: lod_terrain::LodTerrainPipeline, - particle: particle::ParticlePipeline, - clouds: clouds::CloudsPipeline, - postprocess: postprocess::PostProcessPipeline, - // Consider reenabling at some time - // player_shadow: figure::FigurePipeline, - skybox: skybox::SkyboxPipeline, - sprite: sprite::SpritePipeline, - terrain: terrain::TerrainPipeline, -} - -pub struct ShadowPipelines { - pub point: Option, - pub directed: Option, - pub figure: Option, -} - -pub struct IngameAndShadowPipelines { - pub ingame: IngamePipelines, - pub shadow: ShadowPipelines, -} - -/// Pipelines neccesary to display the UI and take screenshots -/// Use to decouple interface pipeline creation when initializing the renderer -pub struct InterfacePipelines { - pub ui: ui::UiPipeline, - pub blit: blit::BlitPipeline, -} - -impl Pipelines { - pub fn consolidate(interface: InterfacePipelines, ingame: IngamePipelines) -> Self { - Self { - debug: ingame.debug, - figure: ingame.figure, - fluid: ingame.fluid, - lod_terrain: ingame.lod_terrain, - particle: ingame.particle, - clouds: ingame.clouds, - postprocess: ingame.postprocess, - //player_shadow: ingame.player_shadow, - skybox: ingame.skybox, - sprite: ingame.sprite, - terrain: ingame.terrain, - ui: interface.ui, - blit: interface.blit, - } - } -} - -/// Processed shaders ready for use in pipeline creation -struct ShaderModules { - skybox_vert: wgpu::ShaderModule, - skybox_frag: wgpu::ShaderModule, - debug_vert: wgpu::ShaderModule, - debug_frag: wgpu::ShaderModule, - figure_vert: wgpu::ShaderModule, - figure_frag: wgpu::ShaderModule, - terrain_vert: wgpu::ShaderModule, - terrain_frag: wgpu::ShaderModule, - fluid_vert: wgpu::ShaderModule, - fluid_frag: wgpu::ShaderModule, - sprite_vert: wgpu::ShaderModule, - sprite_frag: wgpu::ShaderModule, - particle_vert: wgpu::ShaderModule, - particle_frag: wgpu::ShaderModule, - ui_vert: wgpu::ShaderModule, - ui_frag: wgpu::ShaderModule, - lod_terrain_vert: wgpu::ShaderModule, - lod_terrain_frag: wgpu::ShaderModule, - clouds_vert: wgpu::ShaderModule, - clouds_frag: wgpu::ShaderModule, - postprocess_vert: wgpu::ShaderModule, - postprocess_frag: wgpu::ShaderModule, - blit_vert: wgpu::ShaderModule, - blit_frag: wgpu::ShaderModule, - point_light_shadows_vert: wgpu::ShaderModule, - light_shadows_directed_vert: wgpu::ShaderModule, - light_shadows_figure_vert: wgpu::ShaderModule, -} - -impl ShaderModules { - pub fn new( - device: &wgpu::Device, - shaders: &Shaders, - mode: &RenderMode, - has_shadow_views: bool, - ) -> Result { - prof_span!(_guard, "ShaderModules::new"); - use shaderc::{CompileOptions, Compiler, OptimizationLevel, ResolvedInclude, ShaderKind}; - - let constants = shaders.get("include.constants").unwrap(); - let globals = shaders.get("include.globals").unwrap(); - let sky = shaders.get("include.sky").unwrap(); - let light = shaders.get("include.light").unwrap(); - let srgb = shaders.get("include.srgb").unwrap(); - let random = shaders.get("include.random").unwrap(); - let lod = shaders.get("include.lod").unwrap(); - let shadows = shaders.get("include.shadows").unwrap(); - - // We dynamically add extra configuration settings to the constants file. - let constants = format!( - r#" -{} - -#define VOXYGEN_COMPUTATION_PREFERENCE {} -#define FLUID_MODE {} -#define CLOUD_MODE {} -#define LIGHTING_ALGORITHM {} -#define SHADOW_MODE {} - -"#, - &constants.0, - // TODO: Configurable vertex/fragment shader preference. - "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT", - match mode.fluid { - FluidMode::Cheap => "FLUID_MODE_CHEAP", - FluidMode::Shiny => "FLUID_MODE_SHINY", - }, - match mode.cloud { - CloudMode::None => "CLOUD_MODE_NONE", - CloudMode::Minimal => "CLOUD_MODE_MINIMAL", - CloudMode::Low => "CLOUD_MODE_LOW", - CloudMode::Medium => "CLOUD_MODE_MEDIUM", - CloudMode::High => "CLOUD_MODE_HIGH", - CloudMode::Ultra => "CLOUD_MODE_ULTRA", - }, - match mode.lighting { - LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN", - LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", - LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN", - }, - match mode.shadow { - ShadowMode::None => "SHADOW_MODE_NONE", - ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP", - ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP", - }, - ); - - let anti_alias = shaders - .get(match mode.aa { - AaMode::None => "antialias.none", - AaMode::Fxaa => "antialias.fxaa", - AaMode::MsaaX4 => "antialias.msaa-x4", - AaMode::MsaaX8 => "antialias.msaa-x8", - AaMode::MsaaX16 => "antialias.msaa-x16", - }) - .unwrap(); - - let cloud = shaders - .get(match mode.cloud { - CloudMode::None => "include.cloud.none", - _ => "include.cloud.regular", - }) - .unwrap(); - - let mut compiler = Compiler::new().ok_or(RenderError::ErrorInitializingCompiler)?; - let mut options = CompileOptions::new().ok_or(RenderError::ErrorInitializingCompiler)?; - options.set_optimization_level(OptimizationLevel::Performance); - options.set_forced_version_profile(430, shaderc::GlslProfile::Core); - options.set_include_callback(move |name, _, shader_name, _| { - Ok(ResolvedInclude { - resolved_name: name.to_string(), - content: match name { - "constants.glsl" => constants.clone(), - "globals.glsl" => globals.0.to_owned(), - "shadows.glsl" => shadows.0.to_owned(), - "sky.glsl" => sky.0.to_owned(), - "light.glsl" => light.0.to_owned(), - "srgb.glsl" => srgb.0.to_owned(), - "random.glsl" => random.0.to_owned(), - "lod.glsl" => lod.0.to_owned(), - "anti-aliasing.glsl" => anti_alias.0.to_owned(), - "cloud.glsl" => cloud.0.to_owned(), - other => { - return Err(format!( - "Include {} in {} is not defined", - other, shader_name - )); - }, - }, - }) - }); - - let mut create_shader = |name, kind| { - let glsl = &shaders - .get(name) - .unwrap_or_else(|| panic!("Can't retrieve shader: {}", name)) - .0; - let file_name = format!("{}.glsl", name); - create_shader_module(device, &mut compiler, glsl, kind, &file_name, &options) - }; - - let selected_fluid_shader = ["fluid-frag.", match mode.fluid { - FluidMode::Cheap => "cheap", - FluidMode::Shiny => "shiny", - }] - .concat(); - - Ok(Self { - skybox_vert: create_shader("skybox-vert", ShaderKind::Vertex)?, - skybox_frag: create_shader("skybox-frag", ShaderKind::Fragment)?, - debug_vert: create_shader("debug-vert", ShaderKind::Vertex)?, - debug_frag: create_shader("debug-frag", ShaderKind::Fragment)?, - figure_vert: create_shader("figure-vert", ShaderKind::Vertex)?, - figure_frag: create_shader("figure-frag", ShaderKind::Fragment)?, - terrain_vert: create_shader("terrain-vert", ShaderKind::Vertex)?, - terrain_frag: create_shader("terrain-frag", ShaderKind::Fragment)?, - fluid_vert: create_shader("fluid-vert", ShaderKind::Vertex)?, - fluid_frag: create_shader(&selected_fluid_shader, ShaderKind::Fragment)?, - sprite_vert: create_shader("sprite-vert", ShaderKind::Vertex)?, - sprite_frag: create_shader("sprite-frag", ShaderKind::Fragment)?, - particle_vert: create_shader("particle-vert", ShaderKind::Vertex)?, - particle_frag: create_shader("particle-frag", ShaderKind::Fragment)?, - ui_vert: create_shader("ui-vert", ShaderKind::Vertex)?, - ui_frag: create_shader("ui-frag", ShaderKind::Fragment)?, - lod_terrain_vert: create_shader("lod-terrain-vert", ShaderKind::Vertex)?, - lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?, - clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?, - clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?, - postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?, - postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?, - blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?, - blit_frag: create_shader("blit-frag", ShaderKind::Fragment)?, - point_light_shadows_vert: create_shader( - "point-light-shadows-vert", - ShaderKind::Vertex, - )?, - light_shadows_directed_vert: create_shader( - "light-shadows-directed-vert", - ShaderKind::Vertex, - )?, - light_shadows_figure_vert: create_shader( - "light-shadows-figure-vert", - ShaderKind::Vertex, - )?, - }) - } -} - -fn create_shader_module( - device: &wgpu::Device, - compiler: &mut shaderc::Compiler, - source: &str, - kind: shaderc::ShaderKind, - file_name: &str, - options: &shaderc::CompileOptions, -) -> Result { - prof_span!(_guard, "create_shader_modules"); - use std::borrow::Cow; - - let spv = compiler - .compile_into_spirv(source, kind, file_name, "main", Some(options)) - .map_err(|e| (file_name, e))?; - - let label = [file_name, "\n\n", source].concat(); - Ok(device.create_shader_module(&wgpu::ShaderModuleDescriptor { - label: Some(&label), - source: wgpu::ShaderSource::SpirV(Cow::Borrowed(spv.as_binary())), - flags: wgpu::ShaderFlags::empty(), - // TODO: renable // flags: wgpu::ShaderFlags::VALIDATION, - })) -} - -/// Things needed to create a pipeline -#[derive(Clone, Copy)] -struct PipelineNeeds<'a> { - device: &'a wgpu::Device, - layouts: &'a Layouts, - shaders: &'a ShaderModules, - mode: &'a RenderMode, - sc_desc: &'a wgpu::SwapChainDescriptor, -} - -/// Creates InterfacePipelines in parallel -fn create_interface_pipelines( - needs: PipelineNeeds, - pool: &rayon::ThreadPool, - tasks: [Task; 2], -) -> InterfacePipelines { - prof_span!(_guard, "create_interface_pipelines"); - - let [ui_task, blit_task] = tasks; - // Construct a pipeline for rendering UI elements - let create_ui = || { - ui_task.run( - || { - ui::UiPipeline::new( - needs.device, - &needs.shaders.ui_vert, - &needs.shaders.ui_frag, - needs.sc_desc, - &needs.layouts.global, - &needs.layouts.ui, - ) - }, - "ui pipeline creation", - ) - }; - - // Construct a pipeline for blitting, used during screenshotting - let create_blit = || { - blit_task.run( - || { - blit::BlitPipeline::new( - needs.device, - &needs.shaders.blit_vert, - &needs.shaders.blit_frag, - needs.sc_desc, - &needs.layouts.blit, - ) - }, - "blit pipeline creation", - ) - }; - - let (ui, blit) = pool.join(create_ui, create_blit); - - InterfacePipelines { ui, blit } -} - -/// Create IngamePipelines and shadow pipelines in parallel -fn create_ingame_and_shadow_pipelines( - needs: PipelineNeeds, - pool: &rayon::ThreadPool, - tasks: [Task; 13], -) -> IngameAndShadowPipelines { - prof_span!(_guard, "create_ingame_and_shadow_pipelines"); - - let PipelineNeeds { - device, - layouts, - shaders, - mode, - sc_desc, - } = needs; - - let [ - debug_task, - skybox_task, - figure_task, - terrain_task, - fluid_task, - sprite_task, - particle_task, - lod_terrain_task, - clouds_task, - postprocess_task, - // TODO: if these are ever actually optionally done, counting them - // as tasks to do beforehand seems kind of iffy since they will just - // be skipped - point_shadow_task, - terrain_directed_shadow_task, - figure_directed_shadow_task, - ] = tasks; - - // TODO: pass in format of target color buffer - - // Pipeline for rendering debug shapes - let create_debug = || { - debug_task.run( - || { - debug::DebugPipeline::new( - device, - &shaders.debug_vert, - &shaders.debug_frag, - &layouts.global, - &layouts.debug, - mode.aa, - ) - }, - "debug pipeline creation", - ) - }; - // Pipeline for rendering skyboxes - let create_skybox = || { - skybox_task.run( - || { - skybox::SkyboxPipeline::new( - device, - &shaders.skybox_vert, - &shaders.skybox_frag, - &layouts.global, - mode.aa, - ) - }, - "skybox pipeline creation", - ) - }; - // Pipeline for rendering figures - let create_figure = || { - figure_task.run( - || { - figure::FigurePipeline::new( - device, - &shaders.figure_vert, - &shaders.figure_frag, - &layouts.global, - &layouts.figure, - mode.aa, - ) - }, - "figure pipeline creation", - ) - }; - // Pipeline for rendering terrain - let create_terrain = || { - terrain_task.run( - || { - terrain::TerrainPipeline::new( - device, - &shaders.terrain_vert, - &shaders.terrain_frag, - &layouts.global, - &layouts.terrain, - mode.aa, - ) - }, - "terrain pipeline creation", - ) - }; - // Pipeline for rendering fluids - let create_fluid = || { - fluid_task.run( - || { - fluid::FluidPipeline::new( - device, - &shaders.fluid_vert, - &shaders.fluid_frag, - &layouts.global, - &layouts.terrain, - mode.aa, - ) - }, - "fluid pipeline creation", - ) - }; - // Pipeline for rendering sprites - let create_sprite = || { - sprite_task.run( - || { - sprite::SpritePipeline::new( - device, - &shaders.sprite_vert, - &shaders.sprite_frag, - &layouts.global, - &layouts.sprite, - &layouts.terrain, - mode.aa, - ) - }, - "sprite pipeline creation", - ) - }; - // Pipeline for rendering particles - let create_particle = || { - particle_task.run( - || { - particle::ParticlePipeline::new( - device, - &shaders.particle_vert, - &shaders.particle_frag, - &layouts.global, - mode.aa, - ) - }, - "particle pipeline creation", - ) - }; - // Pipeline for rendering terrain - let create_lod_terrain = || { - lod_terrain_task.run( - || { - lod_terrain::LodTerrainPipeline::new( - device, - &shaders.lod_terrain_vert, - &shaders.lod_terrain_frag, - &layouts.global, - mode.aa, - ) - }, - "lod terrain pipeline creation", - ) - }; - // Pipeline for rendering our clouds (a kind of post-processing) - let create_clouds = || { - clouds_task.run( - || { - clouds::CloudsPipeline::new( - device, - &shaders.clouds_vert, - &shaders.clouds_frag, - &layouts.global, - &layouts.clouds, - mode.aa, - ) - }, - "clouds pipeline creation", - ) - }; - // Pipeline for rendering our post-processing - let create_postprocess = || { - postprocess_task.run( - || { - postprocess::PostProcessPipeline::new( - device, - &shaders.postprocess_vert, - &shaders.postprocess_frag, - sc_desc, - &layouts.global, - &layouts.postprocess, - ) - }, - "postprocess pipeline creation", - ) - }; - - // - // // Pipeline for rendering the player silhouette - // let player_shadow_pipeline = create_pipeline( - // factory, - // figure::pipe::Init { - // tgt_depth: (gfx::preset::depth::PASS_TEST/*, - // Stencil::new( - // Comparison::Equal, - // 0xff, - // (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep), - // ),*/), - // ..figure::pipe::new() - // }, - // &figure_vert, - // &Glsl::load_watched( - // "voxygen.shaders.player-shadow-frag", - // shader_reload_indicator, - // ) - // .unwrap(), - // &include_ctx, - // gfx::state::CullFace::Back, - // )?; - - // Pipeline for rendering point light terrain shadow maps. - let create_point_shadow = || { - point_shadow_task.run( - || { - shadow::PointShadowPipeline::new( - device, - &shaders.point_light_shadows_vert, - &layouts.global, - &layouts.terrain, - mode.aa, - ) - }, - "point shadow pipeline creation", - ) - }; - // Pipeline for rendering directional light terrain shadow maps. - let create_terrain_directed_shadow = || { - terrain_directed_shadow_task.run( - || { - shadow::ShadowPipeline::new( - device, - &shaders.light_shadows_directed_vert, - &layouts.global, - &layouts.terrain, - mode.aa, - ) - }, - "terrain directed shadow pipeline creation", - ) - }; - // Pipeline for rendering directional light figure shadow maps. - let create_figure_directed_shadow = || { - figure_directed_shadow_task.run( - || { - shadow::ShadowFigurePipeline::new( - device, - &shaders.light_shadows_figure_vert, - &layouts.global, - &layouts.figure, - mode.aa, - ) - }, - "figure directed shadow pipeline creation", - ) - }; - - let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure)); - let j2 = || pool.join(create_terrain, create_fluid); - let j3 = || pool.join(create_sprite, create_particle); - let j4 = || pool.join(create_lod_terrain, create_clouds); - let j5 = || pool.join(create_postprocess, create_point_shadow); - let j6 = || { - pool.join( - create_terrain_directed_shadow, - create_figure_directed_shadow, - ) - }; - - // Ignore this - let ( - ( - ((debug, (skybox, figure)), (terrain, fluid)), - ((sprite, particle), (lod_terrain, clouds)), - ), - ((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)), - ) = pool.join( - || pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)), - || pool.join(j5, j6), - ); - - IngameAndShadowPipelines { - ingame: IngamePipelines { - debug, - figure, - fluid, - lod_terrain, - particle, - clouds, - postprocess, - skybox, - sprite, - terrain, - // player_shadow_pipeline, - }, - // TODO: skip creating these if the shadow map setting is not enabled - shadow: ShadowPipelines { - point: Some(point_shadow), - directed: Some(terrain_directed_shadow), - figure: Some(figure_directed_shadow), - }, - } -} - -/// Creates all the pipelines used to render. -/// Use this for the initial creation. -/// It blocks the main thread to create the interface pipelines while moving the -/// creation of other pipelines into the background -/// NOTE: this tries to use all the CPU cores to complete as soon as possible -pub(super) fn initial_create_pipelines( - device: Arc, - layouts: Arc, - shaders: Shaders, - mode: RenderMode, - sc_desc: wgpu::SwapChainDescriptor, - has_shadow_views: bool, -) -> Result< - ( - InterfacePipelines, - PipelineCreation, - ), - RenderError, -> { - prof_span!(_guard, "initial_create_pipelines"); - - // Process shaders into modules - let shader_modules = ShaderModules::new(&device, &shaders, &mode, has_shadow_views)?; - - // Create threadpool for parallel portion - let pool = rayon::ThreadPoolBuilder::new() - .thread_name(|n| format!("pipeline-creation-{}", n)) - .build() - .unwrap(); - - let needs = PipelineNeeds { - device: &device, - layouts: &layouts, - shaders: &shader_modules, - mode: &mode, - sc_desc: &sc_desc, - }; - - // Create interface pipelines while blocking the main thread - // Note: we use a throwaway Progress tracker here since we don't need to track - // the progress - let interface_pipelines = - create_interface_pipelines(needs, &pool, Progress::new().create_tasks()); - - let pool = Arc::new(pool); - let send_pool = Arc::clone(&pool); - // Track pipeline creation progress - let progress = Arc::new(Progress::new()); - let (pipeline_send, pipeline_recv) = crossbeam_channel::bounded(0); - let pipeline_creation = PipelineCreation { - progress: Arc::clone(&progress), - recv: pipeline_recv, - }; - // Start background compilation - pool.spawn(move || { - let pool = &*send_pool; - - let needs = PipelineNeeds { - device: &device, - layouts: &layouts, - shaders: &shader_modules, - mode: &mode, - sc_desc: &sc_desc, - }; - - let pipelines = create_ingame_and_shadow_pipelines(needs, &pool, progress.create_tasks()); - - pipeline_send.send(pipelines).expect("Channel disconnected"); - }); - - Ok((interface_pipelines, pipeline_creation)) -} - -/// Creates all the pipelines used to render. -/// Use this to recreate all the pipelines in the background. -/// TODO: report progress -/// NOTE: this tries to use all the CPU cores to complete as soon as possible -pub(super) fn recreate_pipelines( - device: Arc, - layouts: Arc, - shaders: Shaders, - mode: RenderMode, - sc_desc: wgpu::SwapChainDescriptor, - has_shadow_views: bool, -) -> PipelineCreation> { - prof_span!(_guard, "recreate_pipelines"); - - // Create threadpool for parallel portion - let pool = rayon::ThreadPoolBuilder::new() - .thread_name(|n| format!("pipeline-recreation-{}", n)) - .build() - .unwrap(); - let pool = Arc::new(pool); - let send_pool = Arc::clone(&pool); - // Track pipeline creation progress - let progress = Arc::new(Progress::new()); - let (result_send, result_recv) = crossbeam_channel::bounded(0); - let pipeline_creation = PipelineCreation { - progress: Arc::clone(&progress), - recv: result_recv, - }; - // Start background compilation - pool.spawn(move || { - let pool = &*send_pool; - - // Create tasks upfront so the total counter will be accurate - let shader_task = progress.create_task(); - let interface_tasks = progress.create_tasks(); - let ingame_and_shadow_tasks = progress.create_tasks(); - - // Process shaders into modules - let guard = shader_task.start("process shaders"); - let shader_modules = match ShaderModules::new(&device, &shaders, &mode, has_shadow_views) { - Ok(modules) => modules, - Err(err) => { - result_send.send(Err(err)).expect("Channel disconnected"); - return; - }, - }; - drop(guard); - - let needs = PipelineNeeds { - device: &device, - layouts: &layouts, - shaders: &shader_modules, - mode: &mode, - sc_desc: &sc_desc, - }; - - // Create interface pipelines - let interface = create_interface_pipelines(needs, &pool, interface_tasks); - - // Create the rest of the pipelines - let IngameAndShadowPipelines { ingame, shadow } = - create_ingame_and_shadow_pipelines(needs, &pool, ingame_and_shadow_tasks); - - // Send them - result_send - .send(Ok((Pipelines::consolidate(interface, ingame), shadow))) - .expect("Channel disconnected"); - }); - - pipeline_creation -} - -use core::sync::atomic::{AtomicUsize, Ordering}; - -/// Represents future task that has not been started -/// Dropping this will mark the task as complete though -struct Task<'a> { - progress: &'a Progress, -} - -/// Represents in-progress task, drop when complete -// NOTE: fields are unused because they are only used for their Drop impls -struct StartedTask<'a> { - _span: common_base::ProfSpan, - _task: Task<'a>, -} - -#[derive(Default)] -struct Progress { - total: AtomicUsize, - complete: AtomicUsize, - // Note: could easily add a "started counter" if that would be useful -} - -impl Progress { - pub fn new() -> Self { Self::default() } - - /// Creates a task incrementing the total number of tasks - /// NOTE: all tasks should be created as upfront as possible so that the - /// total reflects the amount of tasks that will need to be completed - pub fn create_task(&self) -> Task { - self.total.fetch_add(1, Ordering::Relaxed); - Task { progress: &self } - } - - /// Helper method for creating tasks to do in bulk - pub fn create_tasks(&self) -> [Task; N] { [(); N].map(|()| self.create_task()) } -} - -impl<'a> Task<'a> { - /// Start a task. - /// The name is used for profiling. - fn start(self, _name: &str) -> StartedTask<'a> { - // _name only used when tracy feature is activated - StartedTask { - _span: { - prof_span!(guard, _name); - guard - }, - _task: self, - } - } - - /// Convenience function to run the provided closure as the task - /// Completing the task when this function returns - fn run(self, task: impl FnOnce() -> T, name: &str) -> T { - let _guard = self.start(name); - task() - } -} - -impl Drop for Task<'_> { - fn drop(&mut self) { self.progress.complete.fetch_add(1, Ordering::Relaxed); } -} - -pub struct PipelineCreation { - progress: Arc, - recv: crossbeam_channel::Receiver, -} - -impl PipelineCreation { - /// Returns the number of pipelines being built and completed - /// (total, complete) - /// NOTE: there is no guarantee that `total >= complete` due to relaxed - /// atomics but this property should hold most of the time - pub fn status(&self) -> (usize, usize) { - let progress = &*self.progress; - ( - progress.total.load(Ordering::Relaxed), - progress.complete.load(Ordering::Relaxed), - ) - } - - /// Checks if the pipelines were completed and returns the result if they - /// were - pub fn try_complete(self) -> Result { - use crossbeam_channel::TryRecvError; - match self.recv.try_recv() { - // Yay! - Ok(t) => Ok(t), - // Normal error, we have not gotten anything yet - Err(TryRecvError::Empty) => Err(self), - // How rude! - Err(TryRecvError::Disconnected) => { - panic!( - "Background thread panicked or dropped the sender without sending anything!" - ); - }, - } - } -} diff --git a/voxygen/src/render/renderer/screenshot.rs b/voxygen/src/render/renderer/screenshot.rs deleted file mode 100644 index 484b83ef4d..0000000000 --- a/voxygen/src/render/renderer/screenshot.rs +++ /dev/null @@ -1,185 +0,0 @@ -use super::super::pipelines::blit; -use tracing::error; - -pub type ScreenshotFn = Box; - -pub struct TakeScreenshot { - bind_group: blit::BindGroup, - view: wgpu::TextureView, - texture: wgpu::Texture, - buffer: wgpu::Buffer, - screenshot_fn: ScreenshotFn, - // Dimensions used for copying from the screenshot texture to a buffer - width: u32, - height: u32, - bytes_per_pixel: u8, -} - -impl TakeScreenshot { - pub fn new( - device: &wgpu::Device, - blit_layout: &blit::BlitLayout, - sampler: &wgpu::Sampler, - // Used to determine the resolution and texture format - sc_desc: &wgpu::SwapChainDescriptor, - // Function that is given the image after downloading it from the GPU - // This is executed in a background thread - screenshot_fn: ScreenshotFn, - ) -> Self { - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("screenshot tex"), - size: wgpu::Extent3d { - width: sc_desc.width, - height: sc_desc.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: sc_desc.format, - usage: wgpu::TextureUsage::COPY_SRC - | wgpu::TextureUsage::SAMPLED - | wgpu::TextureUsage::RENDER_ATTACHMENT, - }); - - let view = texture.create_view(&wgpu::TextureViewDescriptor { - label: Some("screenshot tex view"), - format: Some(sc_desc.format), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }); - - let bind_group = blit_layout.bind(device, &view, sampler); - - let bytes_per_pixel = sc_desc.format.describe().block_size; - let padded_bytes_per_row = padded_bytes_per_row(sc_desc.width, bytes_per_pixel); - - let buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("screenshot download buffer"), - size: (padded_bytes_per_row * sc_desc.height) as u64, - usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ, - mapped_at_creation: false, - }); - - Self { - bind_group, - texture, - view, - buffer, - screenshot_fn, - width: sc_desc.width, - height: sc_desc.height, - bytes_per_pixel, - } - } - - /// Get the texture view for the screenshot - /// This can then be used as a render attachment - pub fn texture_view(&self) -> &wgpu::TextureView { &self.view } - - /// Get the bind group used for blitting the screenshot to the current - /// swapchain image - pub fn bind_group(&self) -> &wgpu::BindGroup { &self.bind_group.bind_group } - - /// NOTE: spawns thread - /// Call this after rendering to the screenshot texture - pub fn download_and_handle(self, encoder: &mut wgpu::CommandEncoder) { - // Calculate padded bytes per row - let padded_bytes_per_row = padded_bytes_per_row(self.width, self.bytes_per_pixel); - // Copy image to a buffer - encoder.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &self.texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - wgpu::ImageCopyBuffer { - buffer: &self.buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: core::num::NonZeroU32::new(padded_bytes_per_row), - rows_per_image: None, - }, - }, - wgpu::Extent3d { - width: self.width, - height: self.height, - depth_or_array_layers: 1, - }, - ); - // Send buffer to another thread for async mapping, downloading, and passing to - // the given handler function (which probably saves it to the disk) - std::thread::Builder::new() - .name("screenshot".into()) - .spawn(move || { - self.download_and_handle_internal(); - }) - .expect("Failed to spawn screenshot thread"); - } - - fn download_and_handle_internal(self) { - // Calculate padded bytes per row - let padded_bytes_per_row = padded_bytes_per_row(self.width, self.bytes_per_pixel); - let singlethread_rt = match tokio::runtime::Builder::new_current_thread().build() { - Ok(rt) => rt, - Err(err) => { - error!(?err, "Could not create tokio runtime"); - return; - }, - }; - - // Map buffer - let buffer_slice = self.buffer.slice(..); - let buffer_map_future = buffer_slice.map_async(wgpu::MapMode::Read); - - // Wait on buffer mapping - let pixel_bytes = match singlethread_rt.block_on(buffer_map_future) { - // Buffer is mapped and we can read it - Ok(()) => { - // Copy to a Vec - let padded_buffer = buffer_slice.get_mapped_range(); - let mut pixel_bytes = Vec::new(); - padded_buffer - .chunks(padded_bytes_per_row as usize) - .map(|padded_chunk| { - &padded_chunk[..self.width as usize * self.bytes_per_pixel as usize] - }) - .for_each(|row| pixel_bytes.extend_from_slice(row)); - pixel_bytes - }, - // Error - Err(err) => { - error!( - ?err, - "Failed to map buffer for downloading a screenshot from the GPU" - ); - return; - }, - }; - - // Construct image - // TODO: support other formats - let image = image::ImageBuffer::, Vec>::from_vec( - self.width, - self.height, - pixel_bytes, - ) - .expect("Failed to create ImageBuffer! Buffer was not large enough. This should not occur"); - let image = image::DynamicImage::ImageBgra8(image); - - // Call supplied handler - (self.screenshot_fn)(image); - } -} - -// Graphics API requires a specific alignment for buffer copies -fn padded_bytes_per_row(width: u32, bytes_per_pixel: u8) -> u32 { - let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; - let unpadded_bytes_per_row = width * bytes_per_pixel as u32; - let padding = (align - unpadded_bytes_per_row % align) % align; - unpadded_bytes_per_row + padding -} diff --git a/voxygen/src/render/renderer/shaders.rs b/voxygen/src/render/renderer/shaders.rs deleted file mode 100644 index eb3a148a89..0000000000 --- a/voxygen/src/render/renderer/shaders.rs +++ /dev/null @@ -1,96 +0,0 @@ -use common::assets::{self, AssetExt, AssetHandle}; -use hashbrown::HashMap; - -/// Load from a GLSL file. -pub struct Glsl(pub String); - -impl From for Glsl { - fn from(s: String) -> Glsl { Glsl(s) } -} - -impl assets::Asset for Glsl { - type Loader = assets::LoadFrom; - - const EXTENSION: &'static str = "glsl"; -} - -// Note: we use this clone to send the shaders to a background thread -// TODO: use Arc-ed asset and clone that instead -#[derive(Clone)] -pub struct Shaders { - shaders: HashMap>, -} - -impl assets::Compound for Shaders { - // TODO: Taking the specifier argument as a base for shaders specifiers - // would allow to use several shaders groups easily - fn load( - _: &assets::AssetCache, - _: &str, - ) -> Result { - let shaders = [ - "include.constants", - "include.globals", - "include.sky", - "include.light", - "include.srgb", - "include.random", - "include.lod", - "include.shadows", - "antialias.none", - "antialias.fxaa", - "antialias.msaa-x4", - "antialias.msaa-x8", - "antialias.msaa-x16", - "include.cloud.none", - "include.cloud.regular", - "figure-vert", - "light-shadows-figure-vert", - "light-shadows-directed-vert", - "point-light-shadows-vert", - "skybox-vert", - "skybox-frag", - "debug-vert", - "debug-frag", - "figure-frag", - "terrain-vert", - "terrain-frag", - "fluid-vert", - "fluid-frag.cheap", - "fluid-frag.shiny", - "sprite-vert", - "sprite-frag", - "particle-vert", - "particle-frag", - "ui-vert", - "ui-frag", - "lod-terrain-vert", - "lod-terrain-frag", - "clouds-vert", - "clouds-frag", - "postprocess-vert", - "postprocess-frag", - "blit-vert", - "blit-frag", - //"player-shadow-frag", - //"light-shadows-geom", - ]; - - let shaders = shaders - .iter() - .map(|shader| { - let full_specifier = ["voxygen.shaders.", shader].concat(); - let asset = AssetExt::load(&full_specifier)?; - Ok((String::from(*shader), asset)) - }) - .collect::, assets::Error>>()?; - - Ok(Self { shaders }) - } -} - -impl Shaders { - pub fn get(&self, shader: &str) -> Option> { - self.shaders.get(shader).map(|a| a.read()) - } -} diff --git a/voxygen/src/render/renderer/shadow_map.rs b/voxygen/src/render/renderer/shadow_map.rs deleted file mode 100644 index b7361adebe..0000000000 --- a/voxygen/src/render/renderer/shadow_map.rs +++ /dev/null @@ -1,278 +0,0 @@ -use super::{ - super::{pipelines::shadow, texture::Texture, RenderError, ShadowMapMode}, - Renderer, -}; -use vek::*; - -/// A type that holds shadow map data. Since shadow mapping may not be -/// supported on all platforms, we try to keep it separate. -pub struct ShadowMapRenderer { - pub directed_depth: Texture, - - pub point_depth: Texture, - - pub point_pipeline: shadow::PointShadowPipeline, - pub terrain_directed_pipeline: shadow::ShadowPipeline, - pub figure_directed_pipeline: shadow::ShadowFigurePipeline, - pub layout: shadow::ShadowLayout, -} - -pub enum ShadowMap { - Enabled(ShadowMapRenderer), - Disabled { - dummy_point: Texture, // Cube texture - dummy_directed: Texture, - }, -} - -impl ShadowMap { - pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, - point: Option, - directed: Option, - figure: Option, - shadow_views: Option<(Texture, Texture)>, - ) -> Self { - if let ( - Some(point_pipeline), - Some(terrain_directed_pipeline), - Some(figure_directed_pipeline), - Some(shadow_views), - ) = (point, directed, figure, shadow_views) - { - let (point_depth, directed_depth) = shadow_views; - - let layout = shadow::ShadowLayout::new(&device); - - Self::Enabled(ShadowMapRenderer { - directed_depth, - point_depth, - - point_pipeline, - terrain_directed_pipeline, - figure_directed_pipeline, - - layout, - }) - } else { - let (dummy_point, dummy_directed) = Self::create_dummy_shadow_tex(&device, &queue); - Self::Disabled { - dummy_point, - dummy_directed, - } - } - } - - fn create_dummy_shadow_tex(device: &wgpu::Device, queue: &wgpu::Queue) -> (Texture, Texture) { - let make_tex = |view_dim, depth| { - let tex = wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: 4, - height: 4, - depth_or_array_layers: depth, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth24Plus, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, - }; - - let view = wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Depth24Plus), - dimension: Some(view_dim), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - let sampler_info = wgpu::SamplerDescriptor { - label: None, - 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::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - ..Default::default() - }; - - Texture::new_raw(device, &tex, &view, &sampler_info) - }; - - let cube_tex = make_tex(wgpu::TextureViewDimension::Cube, 6); - let tex = make_tex(wgpu::TextureViewDimension::D2, 1); - - // Clear to 1.0 - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Dummy shadow tex clearing encoder"), - }); - let mut clear = |tex: &Texture| { - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Clear dummy shadow texture"), - color_attachments: &[], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &tex.view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }), - }); - }; - clear(&cube_tex); - clear(&tex); - drop(clear); - queue.submit(std::iter::once(encoder.finish())); - - (cube_tex, tex) - } - - /// Create textures and views for shadow maps. - /// Returns (point, directed) - pub(super) fn create_shadow_views( - device: &wgpu::Device, - size: (u32, u32), - mode: &ShadowMapMode, - ) -> Result<(Texture, Texture), RenderError> { - // (Attempt to) apply resolution factor to shadow map resolution. - let resolution_factor = mode.resolution.clamped(0.25, 4.0); - - let max_texture_size = Renderer::max_texture_size_raw(device); - // Limit to max texture size, rather than erroring. - let size = Vec2::new(size.0, size.1).map(|e| { - let size = e as f32 * resolution_factor; - // NOTE: We know 0 <= e since we clamped the resolution factor to be between - // 0.25 and 4.0. - if size <= max_texture_size as f32 { - size as u32 - } else { - max_texture_size - } - }); - - let levels = 1; - // Limit to max texture size rather than erroring. - let two_size = size.map(|e| { - u32::checked_next_power_of_two(e) - .filter(|&e| e <= max_texture_size) - .unwrap_or(max_texture_size) - }); - let min_size = size.reduce_min(); - let max_size = size.reduce_max(); - let _min_two_size = two_size.reduce_min(); - let _max_two_size = two_size.reduce_max(); - // For rotated shadow maps, the maximum size of a pixel along any axis is the - // size of a diagonal along that axis. - let diag_size = size.map(f64::from).magnitude(); - let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size; - let (diag_size, _diag_cross_size) = - if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) { - // NOTE: diag_cross_size must be non-negative, since it is the ratio of a - // non-negative and a positive number (if max_size were zero, - // diag_size would be 0 too). And it must be <= diag_size, - // since min_size <= max_size. Therefore, if diag_size fits in a - // u16, so does diag_cross_size. - (diag_size as u32, diag_cross_size as u32) - } else { - // Limit to max texture resolution rather than error. - (max_texture_size as u32, max_texture_size as u32) - }; - let diag_two_size = u32::checked_next_power_of_two(diag_size) - .filter(|&e| e <= max_texture_size) - // Limit to max texture resolution rather than error. - .unwrap_or(max_texture_size); - - let point_shadow_tex = wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: diag_two_size / 4, - height: diag_two_size / 4, - depth_or_array_layers: 6, - }, - mip_level_count: levels, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth24Plus, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, - }; - - let point_shadow_view = wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Depth24Plus), - dimension: Some(wgpu::TextureViewDimension::Cube), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - let directed_shadow_tex = wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: diag_two_size, - height: diag_two_size, - depth_or_array_layers: 1, - }, - mip_level_count: levels, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Depth24Plus, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, - }; - - let directed_shadow_view = wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Depth24Plus), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::DepthOnly, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - let sampler_info = wgpu::SamplerDescriptor { - label: None, - 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::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - ..Default::default() - }; - - let point_shadow_tex = - Texture::new_raw(device, &point_shadow_tex, &point_shadow_view, &sampler_info); - let directed_shadow_tex = Texture::new_raw( - device, - &directed_shadow_tex, - &directed_shadow_view, - &sampler_info, - ); - - Ok((point_shadow_tex, directed_shadow_tex)) - } - - pub fn textures(&self) -> (&Texture, &Texture) { - match self { - Self::Enabled(renderer) => (&renderer.point_depth, &renderer.directed_depth), - Self::Disabled { - dummy_point, - dummy_directed, - } => (dummy_point, dummy_directed), - } - } - - pub fn is_enabled(&self) -> bool { matches!(self, Self::Enabled(_)) } -} diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index e15911316e..ae9edbced1 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -1,236 +1,186 @@ -use super::RenderError; -use core::num::NonZeroU32; +use super::{gfx_backend, RenderError}; +use gfx::{self, traits::Factory}; use image::{DynamicImage, GenericImageView}; -use wgpu::Extent3d; +use vek::Vec2; + +type DefaultShaderFormat = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb); /// Represents an image that has been uploaded to the GPU. -pub struct Texture { - pub tex: wgpu::Texture, - pub view: wgpu::TextureView, - pub sampler: wgpu::Sampler, - size: Extent3d, - /// TODO: consider making Texture generic over the format - format: wgpu::TextureFormat, +#[derive(Clone)] +pub struct Texture +where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, +{ + pub tex: gfx::handle::Texture::Surface>, + pub srv: gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, + >, + pub sampler: gfx::handle::Sampler, } -impl Texture { +impl Texture +where + F::Surface: gfx::format::TextureSurface, + F::Channel: gfx::format::TextureChannel, + ::DataType: Copy, +{ pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, + factory: &mut gfx_backend::Factory, image: &DynamicImage, - filter_method: Option, - address_mode: Option, + filter_method: Option, + wrap_mode: Option, + border: Option, ) -> Result { - let format = match &image { - DynamicImage::ImageLuma8(_) => wgpu::TextureFormat::R8Unorm, - DynamicImage::ImageLumaA8(_) => panic!("ImageLuma8 unsupported"), - DynamicImage::ImageRgb8(_) => panic!("ImageRgb8 unsupported"), - DynamicImage::ImageRgba8(_) => wgpu::TextureFormat::Rgba8UnormSrgb, - DynamicImage::ImageBgr8(_) => panic!("ImageBgr8 unsupported"), - DynamicImage::ImageBgra8(_) => panic!("ImageBgra8 unsupported"), - DynamicImage::ImageLuma16(_) => panic!("ImageLuma16 unsupported"), - DynamicImage::ImageLumaA16(_) => panic!("ImageLumaA16 unsupported"), - DynamicImage::ImageRgb16(_) => panic!("ImageRgb16 unsupported"), - DynamicImage::ImageRgba16(_) => panic!("ImageRgba16 unsupported"), - }; - - // TODO: Actually handle images that aren't in rgba format properly. + // TODO: Actualy handle images that aren't in rgba format properly. let buffer = image.as_flat_samples_u8().ok_or_else(|| { RenderError::CustomError( "We currently do not support color formats using more than 4 bytes / pixel.".into(), ) })?; + let (tex, srv) = factory + .create_texture_immutable_u8::( + gfx::texture::Kind::D2( + image.width() as u16, + image.height() as u16, + gfx::texture::AaMode::Single, + ), + gfx::texture::Mipmap::Provided, + // Guarenteed to be correct, since all the conversions from DynamicImage to + // FlatSamples go through the underlying ImageBuffer's implementation of + // as_flat_samples(), which guarantees that the resulting FlatSamples is + // well-formed. + &[buffer.as_slice()], + ) + .map_err(RenderError::CombinedError)?; - let bytes_per_pixel = u32::from(buffer.layout.channels); - - let size = Extent3d { - width: image.width(), - height: image.height(), - depth_or_array_layers: 1, - }; - - let tex = device.create_texture(&wgpu::TextureDescriptor { - label: None, - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, - }); - - queue.write_texture( - wgpu::ImageCopyTexture { - texture: &tex, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - buffer.as_slice(), - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: NonZeroU32::new(image.width() * bytes_per_pixel), - rows_per_image: NonZeroU32::new(image.height()), - }, - wgpu::Extent3d { - width: image.width(), - height: image.height(), - depth_or_array_layers: 1, - }, + let mut sampler_info = gfx::texture::SamplerInfo::new( + filter_method.unwrap_or(gfx::texture::FilterMethod::Scale), + wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp), ); - - let sampler_info = wgpu::SamplerDescriptor { - label: None, - address_mode_u: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge), - address_mode_v: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge), - address_mode_w: address_mode.unwrap_or(wgpu::AddressMode::ClampToEdge), - mag_filter: filter_method.unwrap_or(wgpu::FilterMode::Nearest), - min_filter: filter_method.unwrap_or(wgpu::FilterMode::Nearest), - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }; - - let view = tex.create_view(&wgpu::TextureViewDescriptor { - label: None, - format: Some(format), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }); - + let transparent = [0.0, 0.0, 0.0, 1.0].into(); + sampler_info.border = border.unwrap_or(transparent); Ok(Self { tex, - view, - sampler: device.create_sampler(&sampler_info), - size, - format, + srv, + sampler: factory.create_sampler(sampler_info), }) } pub fn new_dynamic( - device: &wgpu::Device, - queue: &wgpu::Queue, - width: u32, - height: u32, - ) -> Self { - let size = wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }; + factory: &mut gfx_backend::Factory, + width: u16, + height: u16, + ) -> Result { + let tex = factory.create_texture( + gfx::texture::Kind::D2( + width, + height, + gfx::texture::AaMode::Single, + ), + 1_u8, + gfx::memory::Bind::SHADER_RESOURCE, + gfx::memory::Usage::Dynamic, + Some(<::Channel as gfx::format::ChannelTyped>::get_channel_type()), + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; - let tex_info = wgpu::TextureDescriptor { - label: None, - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - // TODO: nondynamic version doesn't seeem to have different usage, unify code? - usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, - }; + let srv = factory + .view_texture_as_shader_resource::(&tex, (0, 0), gfx::format::Swizzle::new()) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?; - let sampler_info = wgpu::SamplerDescriptor { - label: None, - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Nearest, - min_filter: wgpu::FilterMode::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }; - - let view_info = wgpu::TextureViewDescriptor { - label: None, - format: Some(wgpu::TextureFormat::Rgba8UnormSrgb), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }; - - let texture = Self::new_raw(device, &tex_info, &view_info, &sampler_info); - texture.clear(queue); // Needs to be fully initialized for partial writes to work on Dx12 AMD - texture - } - - /// Note: the user is responsible for making sure the texture is fully - /// initialized before doing partial writes on Dx12 AMD: https://github.com/gfx-rs/wgpu/issues/1306 - pub fn new_raw( - device: &wgpu::Device, - texture_info: &wgpu::TextureDescriptor, - view_info: &wgpu::TextureViewDescriptor, - sampler_info: &wgpu::SamplerDescriptor, - ) -> Self { - let tex = device.create_texture(texture_info); - let view = tex.create_view(view_info); - - Self { + Ok(Self { tex, - view, - sampler: device.create_sampler(sampler_info), - size: texture_info.size, - format: texture_info.format, - } + srv, + sampler: factory.create_sampler(gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Scale, + gfx::texture::WrapMode::Clamp, + )), + }) } - /// Clears the texture data to 0 - pub fn clear(&self, queue: &wgpu::Queue) { - let size = self.size; - let byte_len = size.width as usize - * size.height as usize - * size.depth_or_array_layers as usize - * self.format.describe().block_size as usize; - let zeros = vec![0; byte_len]; + pub fn new_immutable_raw( + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + mipmap: gfx::texture::Mipmap, + data: &[&[::DataType]], + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let (tex, srv) = factory + .create_texture_immutable::(kind, mipmap, data) + .map_err(RenderError::CombinedError)?; - self.update(queue, [0, 0], [size.width, size.height], &zeros); + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) + } + + pub fn new_raw( + _device: &mut gfx_backend::Device, + factory: &mut gfx_backend::Factory, + kind: gfx::texture::Kind, + max_levels: u8, + bind: gfx::memory::Bind, + usage: gfx::memory::Usage, + levels: (u8, u8), + swizzle: gfx::format::Swizzle, + sampler_info: gfx::texture::SamplerInfo, + ) -> Result { + let tex = factory + .create_texture( + kind, + max_levels as gfx::texture::Level, + bind | gfx::memory::Bind::SHADER_RESOURCE, + usage, + Some(<::Channel as gfx::format::ChannelTyped>::get_channel_type()) + ) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?; + + let srv = factory + .view_texture_as_shader_resource::(&tex, levels, swizzle) + .map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?; + + Ok(Self { + tex, + srv, + sampler: factory.create_sampler(sampler_info), + }) } /// Update a texture with the given data (used for updating the glyph cache /// texture). - pub fn update(&self, queue: &wgpu::Queue, offset: [u32; 2], size: [u32; 2], data: &[u8]) { - let bytes_per_pixel = self.format.describe().block_size as u32; - debug_assert_eq!( - data.len(), - size[0] as usize * size[1] as usize * bytes_per_pixel as usize - ); - // TODO: Only works for 2D images - queue.write_texture( - wgpu::ImageCopyTexture { - texture: &self.tex, - mip_level: 0, - origin: wgpu::Origin3d { - x: offset[0], - y: offset[1], - z: 0, - }, - }, - data, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: NonZeroU32::new(size[0] * bytes_per_pixel), - rows_per_image: NonZeroU32::new(size[1]), - }, - wgpu::Extent3d { - width: size[0], - height: size[1], - depth_or_array_layers: 1, - }, - ); + pub fn update( + &self, + encoder: &mut gfx::Encoder, + offset: [u16; 2], + size: [u16; 2], + data: &[::DataType], + ) -> Result<(), RenderError> { + let info = gfx::texture::ImageInfoCommon { + xoffset: offset[0], + yoffset: offset[1], + zoffset: 0, + width: size[0], + height: size[1], + depth: 0, + format: (), + mipmap: 0, + }; + encoder + .update_texture::<::Surface, F>( + &self.tex, None, info, data, + ) + .map_err(RenderError::TexUpdateError) } /// Get dimensions of the represented image. - pub fn get_dimensions(&self) -> vek::Vec3 { - vek::Vec3::new( - self.size.width, - self.size.height, - self.size.depth_or_array_layers, - ) + pub fn get_dimensions(&self) -> Vec2 { + let (w, h, ..) = self.tex.get_info().kind.get_dimensions(); + Vec2::new(w, h) } } diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 35137ecad0..d04a3958fe 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -170,10 +170,19 @@ fn handle_main_events_cleared( if let Some(last) = states.last_mut() { span!(guard, "Render"); let renderer = global_state.window.renderer_mut(); + // Clear the shadow maps. + renderer.clear_shadows(); + // Clear the screen + renderer.clear(); // Render the screen using the global renderer last.render(renderer, &global_state.settings); - capped_fps = last.capped_fps(); - + // Finish the frame. + global_state.window.renderer_mut().flush(); + // Display the frame on the window. + global_state + .window + .swap_buffers() + .expect("Failed to swap window buffers!"); drop(guard); } diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index d4aa8aa752..fc34f8c93b 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -1,12 +1,11 @@ use common::{terrain::TerrainGrid, vol::ReadVol}; use common_base::span; -use core::{f32::consts::PI, fmt::Debug}; -use num::traits::{real::Real, FloatConst}; +use std::f32::consts::PI; use treeculler::Frustum; use vek::*; -pub const NEAR_PLANE: f32 = 0.0625; -pub const FAR_PLANE: f32 = 524288.06; // excessive precision: 524288.0625 +pub const NEAR_PLANE: f32 = 0.25; +pub const FAR_PLANE: f32 = 100000.0; const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -32,9 +31,6 @@ pub struct Dependents { pub view_mat_inv: Mat4, pub proj_mat: Mat4, pub proj_mat_inv: Mat4, - /// Specifically there for satisfying our treeculler dependency, which can't - /// handle inverted depth planes. - pub proj_mat_treeculler: Mat4, pub cam_pos: Vec3, pub cam_dir: Vec3, } @@ -68,249 +64,6 @@ fn clamp_and_modulate(ori: Vec3) -> Vec3 { } } -/// Generalized method to construct a perspective projection with x ∈ [-1,1], y -/// ∈ [-1,1], z ∈ [0,1] given fov_y_radians, aspect_ratio, 1/n, and 1/f. Note -/// that you pass in *1/n* and *1/f*, not n and f like you normally would for a -/// perspective projection; this is done to enable uniform handling of both -/// finite and infinite far planes. -/// -/// The only requirements on n and f are: 1/n ≠ 1/f, and 0 ≤ 1/n * 1/f. -/// -/// This ensures that the near and far plane are not identical (or else your -/// projection would not cover any distance), and that they have the same sign -/// (or else we cannot rely on clipping to properly fix your scene). This also -/// ensures that at least one of 1/n and 1/f is not 0, and by construction it -/// guarantees that neither n nor f are 0; these are required in order to make -/// sense of the definition of near and far planes, and avoid collapsing all -/// depths to a single point. -/// -/// For "typical" projections (matching perspective_lh_no), you would satisfy -/// the stronger requirements. We give the typical conditions for each bullet -/// point, and then explain the consequences of not satisfying these conditions: -/// -/// * 1/n < 1/f (0 to 1 depth planes, meaning n = near and f = far; if f < n, -/// depth planes go from 1 to 0, meaning f = near and n = far, aka "reverse -/// depth"). -/// -/// This is by far the most -/// likely thing to want to change; inverted depth coordinates have *far* -/// better accuracy for DirectX / Metal / WGPU-like APIs, when using -/// floating point depth, while not being *worse* than the alternative -/// (OpenGL-like depth, or when using fixed-point / integer depth). For -/// maximum benefit, make sure you are using Depth32F, as on most platforms -/// this is the only depth buffer size where floating point can be used. -/// -/// It is a bit unintuitive to prove this, but it turns out that when using -/// 1 to 0 depth planes, the point where the depth buffer has its worst -/// precision is not at the far plane (as with 0 to 1 depth planes) nor at -/// the near plane, as you might expect, but exactly at far/2 (the -/// near plane setting does not affect the point of minimum accuracy at -/// all!). However, don't let this fool you into believing the point of -/// worst precision has simply been moved around--for *any* fixed Δz that is -/// the minimum amount of depth precision you want over the whole range, and -/// any near plane, you can set the far plane farther (generally much much -/// farther!) with reversed clip space than you can with standard clip space -/// while still getting at least that much depth precision in the worst -/// case. Nor is this a small worst-case; for many desirable near and far -/// plane combinations, more than half the visible space will have -/// completely unusable precision under 0 to 1 depth, while having much better -/// than needed precision under 1 to 0 depth. -/// -/// To compute the exact (at least "roughly exact") worst-case accuracy for -/// floating point depth and a given precision target Δz, for reverse clip -/// planes (this can be computed for the non-reversed case too, but it's -/// painful and the values are horrible, so don't bother), we compute -/// (assuming a finite far plane--see below for details on the infinite -/// case) the change in the integer representation of the mantissa at z=n/2: -/// -/// ```ignore -/// e = floor(ln(near/(far - near))/ln(2)) -/// db/dz = 2^(2-e) / ((1 / far - 1 / near) * (far)^2) -/// ``` -/// -/// Then the maximum precision you can safely use to get a change in the -/// integer representation of the mantissa (assuming 32-bit floating points) -/// is around: -/// -/// ```ignore -/// abs(2^(-23) / (db/dz)). -/// ``` -/// -/// In particular, if your worst-case target accuracy over the depth range -/// is Δz, you should be okay if: -/// -/// ```ignore -/// abs(Δz * (db/dz)) * 2^(23) ≥ 1. -/// ``` -/// -/// This only accounts for precision of the final floating-point value, so -/// it's possible that artifacts may be introduced elsewhere during the -/// computation that reduce precision further; the most famous example of -/// this is that OpenGL wipes out most of the precision gains by going from -/// [-1,1] to [0,1] by letting -/// -/// ```ignore -/// clip space depth = depth * 0.5 + 0.5 -/// ``` -/// -/// which results in huge precision errors by removing nearly all the -/// floating point values with the most precision (those close to 0). -/// Fortunately, most such artifacts are absent under the wgpu/DirectX/Metal -/// depth clip space model, so with any luck remaining depth errors due to -/// the perspective warp itself should be minimal. -/// -/// * 0 ≠ 1/far (finite far plane). When this is false, the far plane is at -/// infinity; this removes the restriction of having a far plane at all, often -/// with minimal reduction in accuracy for most values in the scene. In fact, -/// in almost all cases with non-reversed depth planes, it *improves* accuracy -/// over the finite case for the vast majority of the range; however, you -/// should be using reversed depth planes, and if you are then there is a -/// quite natural accuracy vs. distance tradeoff in the infinite case. -/// -/// When using an infinite far plane, the worst-case accuracy is *always* at -/// infinity, and gets progressively worse as you get farther away from the -/// near plane. However, there is a second advantage that may not be -/// immediately apparent: the perspective warp becomes much simpler, -/// potentially removing artifacts! Specifically, in the 0 to 1 depth plane -/// case, the assigned depth value (after perspective division) becomes: -/// -/// ```ignore -/// depth = 1 - near/z -/// ``` -/// -/// while in the 1 to 0 depth plane case (which you should be using), the -/// equation is even simpler: -/// -/// ```ignore -/// depth = near/z -/// ``` -/// -/// In the 1 to 0 case, in particular, you can see that the depth value is -/// *linear in z in log space.* This lets us compute, for any given target -/// precision, a *very* simple worst-case upper bound on the maximum -/// absolute z value for which that precision can be achieved (the upper -/// bound is tight in some cases, but in others may be conservative): -/// -/// ```ignore -/// db/dz ≥ 1/z -/// ``` -/// -/// Plugging that into our old formula, we find that we attain the required -/// precision at least in the range (again, this is for the 1 to 0 infinite -/// case only!): -/// -/// ```ignore -/// abs(z) ≤ Δz * 2^23 -/// ``` -/// -/// One thing you may notice is that this worst-case bound *does not depend -/// on the near plane.* This means that (within reason) you can put the near -/// plane as close as you like and still attain this bound. Of course, the -/// bound is not completely tight, but it should not be off by more than a -/// factor of 2 or so (informally proven, not made rigorous yet), so for most -/// practical purposes you can set the near plane as low as you like in this -/// case. -/// -/// * 0 < 1/near (positive near plane--best used when moving *to* left-handed -/// spaces, as we normally do in OpenGL and DirectX). A use case for *not* -/// doing this is that it allows moving *from* a left-handed space *to* a -/// right-handed space in WGPU / DirectX / Metal coordinates; this means that -/// if matrices were already set up for OpenGL using functions like look_at_rh -/// that assume right-handed coordinates, we can simply switch these to -/// look_at_lh and use a right-handed perspective projection with a negative -/// near plane, to get correct rendering behavior. Details are out of scope -/// for this comment. -/// -/// Note that there is one final, very important thing that affects possible -/// precision--the actual underlying precision of the floating point format at a -/// particular value! As your z values go up, their precision will shrink, so -/// if at all possible try to shrink your z values down to the lowest range in -/// which they can be. Unfortunately, this cannot be part of the perspective -/// projection itself, because by the time z gets to the projection it is -/// usually too late for values to still be integers (or coarse-grained powers -/// of 2). Instead, try to scale down x, y, and z as soon as possible before -/// submitting them to the GPU, ideally by as large as possible of a power of 2 -/// that works for your use case. Not only will this improve depth precision -/// and recall, it will also help address other artifacts caused by values far -/// from z (such as improperly rounded rotations, or improper line equations due -/// to greedy meshing). -/// -/// TODO: Consider passing fractions rather than 1/n and 1/f directly, even -/// though the logic for why it should be okay to pass them directly is probably -/// sound (they are both valid z values in the range, so gl_FragCoord.w will be -/// assigned to this, meaning if they are imprecise enough then the whole -/// calculation will be similarly imprecies). -/// -/// TODO: Since it's a bit confusing that n and f are not always near and far, -/// and a negative near plane can (probably) be emulated with simple actions on -/// the perspective matrix, consider removing this functionailty and replacing -/// our assertion with a single condition: `(1/far) * (1/near) < (1/near)²`. -pub fn perspective_lh_zo_general( - fov_y_radians: T, - aspect_ratio: T, - inv_n: T, - inv_f: T, -) -> Mat4 -where - T: Real + FloatConst + Debug, -{ - // Per comments, we only need these two assertions to make sure our calculations - // make sense. - debug_assert_ne!( - inv_n, inv_f, - "The near and far plane distances cannot be equal, found: {:?} = {:?}", - inv_n, inv_f - ); - debug_assert!( - T::zero() <= inv_n * inv_f, - "The near and far plane distances must have the same sign, found: {:?} * {:?} < 0", - inv_n, - inv_f - ); - - // TODO: Would be nice to separate out the aspect ratio computations. - let two = T::one() + T::one(); - let tan_half_fovy = (fov_y_radians / two).tan(); - let m00 = T::one() / (aspect_ratio * tan_half_fovy); - let m11 = T::one() / tan_half_fovy; - let m23 = -T::one() / (inv_n - inv_f); - let m22 = inv_n * (-m23); - Mat4::new( - m00, - T::zero(), - T::zero(), - T::zero(), - T::zero(), - m11, - T::zero(), - T::zero(), - T::zero(), - T::zero(), - m22, - m23, - T::zero(), - T::zero(), - T::one(), - T::zero(), - ) -} - -/// Same as perspective_lh_zo_general, but for right-handed source spaces. -pub fn perspective_rh_zo_general( - fov_y_radians: T, - aspect_ratio: T, - inv_n: T, - inv_f: T, -) -> Mat4 -where - T: Real + FloatConst + Debug, -{ - let mut m = perspective_lh_zo_general(fov_y_radians, aspect_ratio, inv_n, inv_f); - m[(2, 2)] = -m[(2, 2)]; - m[(3, 2)] = -m[(3, 2)]; - m -} - impl Camera { /// Create a new `Camera` with default parameters. pub fn new(aspect: f32, mode: CameraMode) -> Self { @@ -336,7 +89,6 @@ impl Camera { view_mat_inv: Mat4::identity(), proj_mat: Mat4::identity(), proj_mat_inv: Mat4::identity(), - proj_mat_treeculler: Mat4::identity(), cam_pos: Vec3::zero(), cam_dir: Vec3::unit_y(), }, @@ -383,25 +135,20 @@ impl Camera { * Mat4::translation_3d(-self.focus.map(|e| e.fract())); self.dependents.view_mat_inv = self.dependents.view_mat.inverted(); - // NOTE: We reverse the far and near planes to produce an inverted depth - // buffer (1 to 0 z planes). self.dependents.proj_mat = - perspective_rh_zo_general(self.fov, self.aspect, 1.0 / FAR_PLANE, 1.0 / NEAR_PLANE); - // For treeculler, we also produce a version without inverted depth. - self.dependents.proj_mat_treeculler = - perspective_rh_zo_general(self.fov, self.aspect, 1.0 / NEAR_PLANE, 1.0 / FAR_PLANE); + Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE); self.dependents.proj_mat_inv = self.dependents.proj_mat.inverted(); // TODO: Make this more efficient. - self.dependents.cam_pos = Vec3::from(self.dependents.view_mat_inv * Vec4::unit_w()); + self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w()); self.frustum = Frustum::from_modelview_projection( - (self.dependents.proj_mat_treeculler + (self.dependents.proj_mat * self.dependents.view_mat * Mat4::translation_3d(-self.focus.map(|e| e.trunc()))) .into_col_arrays(), ); - self.dependents.cam_dir = Vec3::from(self.dependents.view_mat_inv * -Vec4::unit_z()); + self.dependents.cam_dir = Vec3::from(self.dependents.view_mat.inverted() * -Vec4::unit_z()); } pub fn frustum(&self) -> &Frustum { &self.frustum } diff --git a/voxygen/src/scene/debug.rs b/voxygen/src/scene/debug.rs deleted file mode 100644 index e4ddf857ac..0000000000 --- a/voxygen/src/scene/debug.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::render::{ - Bound, Consts, DebugDrawer, DebugLocals, DebugVertex, Mesh, Model, Quad, Renderer, Tri, -}; -use common::util::srgba_to_linear; -use hashbrown::{HashMap, HashSet}; -use tracing::warn; -use vek::*; - -#[derive(Debug)] -pub enum DebugShape { - Line([Vec3; 2]), - Cylinder { radius: f32, height: f32 }, -} - -impl DebugShape { - pub fn mesh(&self) -> Mesh { - use core::f32::consts::PI; - let mut mesh = Mesh::new(); - let tri = |x: Vec3, y: Vec3, z: Vec3| { - Tri::::new(x.into(), y.into(), z.into()) - }; - let quad = |x: Vec3, y: Vec3, z: Vec3, w: Vec3| { - Quad::::new(x.into(), y.into(), z.into(), w.into()) - }; - match self { - DebugShape::Line([a, b]) => { - let h = Vec3::new(0.0, 1.0, 0.0); - mesh.push_quad(quad(*a, a + h, b + h, *b)); - }, - DebugShape::Cylinder { radius, height } => { - const SUBDIVISIONS: usize = 16; - for i in 0..SUBDIVISIONS { - let angle = |j: usize| (j as f32 / SUBDIVISIONS as f32) * 2.0 * PI; - let a = Vec3::zero(); - let b = Vec3::new(radius * angle(i).cos(), radius * angle(i).sin(), 0.0); - let c = Vec3::new( - radius * angle(i + 1).cos(), - radius * angle(i + 1).sin(), - 0.0, - ); - let h = Vec3::new(0.0, 0.0, *height); - mesh.push_tri(tri(a, b, c)); - mesh.push_quad(quad(b, c, c + h, b + h)); - mesh.push_tri(tri(a + h, b + h, c + h)); - } - }, - } - mesh - } -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct DebugShapeId(u64); - -pub struct Debug { - next_shape_id: DebugShapeId, - pending_shapes: HashMap, - pending_locals: HashMap, - pending_deletes: HashSet, - models: HashMap, Bound>)>, -} - -impl Debug { - pub fn new() -> Debug { - Debug { - next_shape_id: DebugShapeId(0), - pending_shapes: HashMap::new(), - pending_locals: HashMap::new(), - pending_deletes: HashSet::new(), - models: HashMap::new(), - } - } - - pub fn add_shape(&mut self, shape: DebugShape) -> DebugShapeId { - let id = DebugShapeId(self.next_shape_id.0); - self.next_shape_id.0 += 1; - self.pending_shapes.insert(id, shape); - id - } - - pub fn set_pos_and_color(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4]) { - self.pending_locals.insert(id, (pos, color)); - } - - pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); } - - pub fn maintain(&mut self, renderer: &mut Renderer) { - for (id, shape) in self.pending_shapes.drain() { - if let Some(model) = renderer.create_model(&shape.mesh()) { - let locals = renderer.create_debug_bound_locals(&[DebugLocals { - pos: [0.0; 4], - color: [1.0, 0.0, 0.0, 1.0], - }]); - self.models.insert(id, (model, locals)); - } else { - warn!( - "Failed to create model for debug shape {:?}: {:?}", - id, shape - ); - } - } - for (id, (pos, color)) in self.pending_locals.drain() { - if let Some((_, locals)) = self.models.get_mut(&id) { - let lc = srgba_to_linear(color.into()); - let new_locals = [DebugLocals { - pos, - color: [lc.r, lc.g, lc.b, lc.a], - }]; - renderer.update_consts(locals, &new_locals); - } else { - warn!( - "Tried to update locals for nonexistent debug shape {:?}", - id - ); - } - } - for id in self.pending_deletes.drain() { - self.models.remove(&id); - } - } - - pub fn render<'a>(&'a self, drawer: &mut DebugDrawer<'_, 'a>) { - for (model, locals) in self.models.values() { - drawer.draw(model, locals); - } - } -} - -impl Default for Debug { - fn default() -> Debug { Debug::new() } -} diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 0165122045..e74ba048a2 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -1,7 +1,9 @@ use super::{load::BodySpec, FigureModelEntry}; use crate::{ - mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain}, - render::{BoneMeshes, ColLightInfo, FigureModel, Mesh, Renderer, TerrainVertex}, + mesh::{greedy::GreedyMesh, Meshable}, + render::{ + BoneMeshes, ColLightInfo, FigureModel, FigurePipeline, Mesh, Renderer, TerrainPipeline, + }, scene::camera::CameraMode, }; use anim::Skeleton; @@ -32,7 +34,7 @@ use vek::*; /// needed to mesh figures. struct MeshWorkerResponse { col_light: ColLightInfo, - opaque: Mesh, + opaque: Mesh, bounds: anim::vek::Aabb, vertex_range: [Range; N], } @@ -370,12 +372,16 @@ where vertex_range, }) = Arc::get_mut(recv).take().and_then(|cell| cell.take()) { - let model_entry = col_lights.create_figure( - renderer, - col_light, - (opaque, bounds), - vertex_range, - ); + // FIXME: We really need to stop hard failing on failure to upload + // to the GPU. + let model_entry = col_lights + .create_figure( + renderer, + col_light, + (opaque, bounds), + vertex_range, + ) + .expect("Failed to upload figure data to the GPU!"); *model = FigureModelEntryFuture::Done(model_entry); // NOTE: Borrow checker isn't smart enough to figure this out. if let FigureModelEntryFuture::Done(model) = model { @@ -405,7 +411,7 @@ where // Then, set up meshing context. let mut greedy = FigureModel::make_greedy(); - let mut opaque = Mesh::::new(); + let mut opaque = Mesh::::new(); // Choose the most conservative bounds for any LOD model. let mut figure_bounds = anim::vek::Aabb { min: anim::vek::Vec3::zero(), @@ -467,57 +473,60 @@ where fn generate_mesh<'a>( greedy: &mut GreedyMesh<'a>, - opaque_mesh: &mut Mesh, + opaque_mesh: &mut Mesh, segment: &'a Segment, offset: Vec3, bone_idx: u8, ) -> BoneMeshes { - let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain( - segment, - (greedy, opaque_mesh, offset, Vec3::one(), bone_idx), - ); + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment, + (greedy, opaque_mesh, offset, Vec3::one(), bone_idx), + ); (opaque, bounds) } fn generate_mesh_lod_mid<'a>( greedy: &mut GreedyMesh<'a>, - opaque_mesh: &mut Mesh, + opaque_mesh: &mut Mesh, segment: &'a Segment, offset: Vec3, bone_idx: u8, ) -> BoneMeshes { let lod_scale = 0.6; - let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain( - segment.scaled_by(Vec3::broadcast(lod_scale)), - ( - greedy, - opaque_mesh, - offset * lod_scale, - Vec3::one() / lod_scale, - bone_idx, - ), - ); + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + bone_idx, + ), + ); (opaque, bounds) } fn generate_mesh_lod_low<'a>( greedy: &mut GreedyMesh<'a>, - opaque_mesh: &mut Mesh, + opaque_mesh: &mut Mesh, segment: &'a Segment, offset: Vec3, bone_idx: u8, ) -> BoneMeshes { let lod_scale = 0.3; - let (opaque, _, _, bounds) = generate_mesh_base_vol_terrain( - segment.scaled_by(Vec3::broadcast(lod_scale)), - ( - greedy, - opaque_mesh, - offset * lod_scale, - Vec3::one() / lod_scale, - bone_idx, - ), - ); + let (opaque, _, _, bounds) = + Meshable::::generate_mesh( + segment.scaled_by(Vec3::broadcast(lod_scale)), + ( + greedy, + opaque_mesh, + offset * lod_scale, + Vec3::one() / lod_scale, + bone_idx, + ), + ); (opaque, bounds) } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 167bbf2571..9817c9522b 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -7,15 +7,14 @@ pub use load::load_mesh; // TODO: Don't make this public. use crate::{ ecs::comp::Interpolated, render::{ - pipelines::{self, ColLights}, - ColLightInfo, FigureBoneData, FigureDrawer, FigureLocals, FigureModel, FigureShadowDrawer, - Mesh, RenderError, Renderer, SubModel, TerrainVertex, + ColLightFmt, ColLightInfo, Consts, FigureBoneData, FigureLocals, FigureModel, GlobalModel, + Mesh, RenderError, Renderer, ShadowPipeline, TerrainPipeline, Texture, }, scene::{ camera::{Camera, CameraMode, Dependents}, math, terrain::Terrain, - SceneData, + LodData, SceneData, }, }; use anim::{ @@ -62,9 +61,10 @@ pub type CameraData<'a> = (&'a Camera, f32); /// Enough data to render a figure model. pub type FigureModelRef<'a> = ( - &'a pipelines::figure::BoundLocals, - SubModel<'a, TerrainVertex>, - &'a ColLights, + &'a Consts, + &'a Consts, + &'a FigureModel, + &'a Texture, ); /// An entry holding enough information to draw or destroy a figure in a @@ -80,20 +80,10 @@ pub struct FigureModelEntry { /// Texture used to store color/light information for this figure entry. /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different * LOD levels. */ - col_lights: ColLights, - /// Vertex ranges stored in this figure entry; there may be several for one - /// figure, because of LOD models. - lod_vertex_ranges: [Range; N], - model: FigureModel, -} - -impl FigureModelEntry { - pub fn lod_model(&self, lod: usize) -> SubModel { - // Note: Range doesn't impl Copy even for trivially Cloneable things - self.model - .opaque - .submodel(self.lod_vertex_ranges[lod].clone()) - } + col_lights: Texture, + /// Models stored in this figure entry; there may be several for one figure, + /// because of LOD models. + pub models: [FigureModel; N], } struct FigureMgrStates { @@ -4709,17 +4699,20 @@ impl FigureMgr { visible_aabb } - pub fn render_shadows<'a>( - &'a self, - drawer: &mut FigureShadowDrawer<'_, 'a>, + pub fn render_shadows( + &self, + renderer: &mut Renderer, state: &State, tick: u64, + global: &GlobalModel, + (is_daylight, _light_data): super::LightData, (camera, figure_lod_render_distance): CameraData, ) { span!(_guard, "render_shadows", "FigureManager::render_shadows"); let ecs = state.ecs(); - ( + if is_daylight && renderer.render_mode().shadow.is_map() { + ( &ecs.entities(), &ecs.read_storage::(), ecs.read_storage::().maybe(), @@ -4732,7 +4725,7 @@ impl FigureMgr { // Don't render dead entities .filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead)) .for_each(|(entity, pos, _, body, _, inventory, scale)| { - if let Some((bound, model, _)) = self.get_model_for_render( + if let Some((locals, bone_consts, model, _)) = self.get_model_for_render( tick, camera, None, @@ -4744,18 +4737,27 @@ impl FigureMgr { figure_lod_render_distance * scale.map_or(1.0, |s| s.0), |state| state.can_shadow_sun(), ) { - drawer.draw(model, bound); + renderer.render_figure_shadow_directed( + model, + global, + locals, + bone_consts, + &global.shadow_mats, + ); } }); + } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - pub fn render<'a>( - &'a self, - drawer: &mut FigureDrawer<'_, 'a>, + pub fn render( + &self, + renderer: &mut Renderer, state: &State, player_entity: EcsEntity, tick: u64, + global: &GlobalModel, + lod: &LodData, (camera, figure_lod_render_distance): CameraData, ) { span!(_guard, "render", "FigureManager::render"); @@ -4764,44 +4766,49 @@ impl FigureMgr { let character_state_storage = state.read_storage::(); let character_state = character_state_storage.get(player_entity); - for (entity, pos, body, _, inventory, scale) in ( + for (entity, pos, _, body, _, inventory, scale) in ( &ecs.entities(), &ecs.read_storage::(), + ecs.read_storage::().maybe(), &ecs.read_storage::(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe() + ecs.read_storage::().maybe(), ) .join() // Don't render dead entities - .filter(|(_, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead)) - // Don't render player - .filter(|(entity, _, _, _, _, _)| *entity != player_entity) + .filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead)) { - if let Some((bound, model, col_lights)) = self.get_model_for_render( - tick, - camera, - character_state, - entity, - body, - inventory, - false, - pos.0, - figure_lod_render_distance * scale.map_or(1.0, |s| s.0), - |state| state.visible(), - ) { - drawer.draw(model, bound, col_lights); + let is_player = entity == player_entity; + + if !is_player { + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( + tick, + camera, + character_state, + entity, + body, + inventory, + false, + pos.0, + figure_lod_render_distance * scale.map_or(1.0, |s| s.0), + |state| state.visible(), + ) { + renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod); + } } } } #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 - pub fn render_player<'a>( - &'a self, - drawer: &mut FigureDrawer<'_, 'a>, + pub fn render_player( + &self, + renderer: &mut Renderer, state: &State, player_entity: EcsEntity, tick: u64, + global: &GlobalModel, + lod: &LodData, (camera, figure_lod_render_distance): CameraData, ) { span!(_guard, "render_player", "FigureManager::render_player"); @@ -4823,7 +4830,7 @@ impl FigureMgr { let inventory_storage = ecs.read_storage::(); let inventory = inventory_storage.get(player_entity); - if let Some((bound, model, col_lights)) = self.get_model_for_render( + if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( tick, camera, character_state, @@ -4835,15 +4842,15 @@ impl FigureMgr { figure_lod_render_distance, |state| state.visible(), ) { - drawer.draw(model, bound, col_lights); - /*renderer.render_player_shadow( + renderer.render_player(model, &col_lights, global, locals, bone_consts, lod); + renderer.render_player_shadow( model, &col_lights, global, bone_consts, lod, &global.shadow_mats, - );*/ + ); } } } @@ -4908,13 +4915,14 @@ impl FigureMgr { }, } = self; let col_lights = &*col_lights_; - if let Some((bound, model_entry)) = match body { + if let Some((locals, bone_consts, model_entry)) = match body { Body::Humanoid(body) => character_states .get(&entity) .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), model_cache.get_model( col_lights, *body, @@ -4930,7 +4938,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), quadruped_small_model_cache.get_model( col_lights, *body, @@ -4946,7 +4955,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), quadruped_medium_model_cache.get_model( col_lights, *body, @@ -4962,7 +4972,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), quadruped_low_model_cache.get_model( col_lights, *body, @@ -4978,7 +4989,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), bird_medium_model_cache.get_model( col_lights, *body, @@ -4994,7 +5006,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), fish_medium_model_cache.get_model( col_lights, *body, @@ -5010,7 +5023,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), theropod_model_cache.get_model( col_lights, *body, @@ -5026,7 +5040,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), dragon_model_cache.get_model( col_lights, *body, @@ -5042,7 +5057,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), bird_large_model_cache.get_model( col_lights, *body, @@ -5058,7 +5074,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), fish_small_model_cache.get_model( col_lights, *body, @@ -5074,7 +5091,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), biped_large_model_cache.get_model( col_lights, *body, @@ -5090,7 +5108,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), biped_small_model_cache.get_model( col_lights, *body, @@ -5106,7 +5125,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), golem_model_cache.get_model( col_lights, *body, @@ -5122,7 +5142,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), object_model_cache.get_model( col_lights, *body, @@ -5138,7 +5159,8 @@ impl FigureMgr { .filter(|state| filter_state(&*state)) .map(move |state| { ( - state.bound(), + state.locals(), + state.bone_consts(), ship_model_cache.get_model( col_lights, *body, @@ -5156,14 +5178,14 @@ impl FigureMgr { let figure_mid_detail_distance = figure_lod_render_distance * 0.5; let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powi(2) { - model_entry.lod_model(2) + &model_entry.models[2] } else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powi(2) { - model_entry.lod_model(1) + &model_entry.models[1] } else { - model_entry.lod_model(0) + &model_entry.models[0] }; - Some((bound, model, col_lights_.texture(model_entry))) + Some((locals, bone_consts, model, col_lights_.texture(model_entry))) } else { // trace!("Body has no saved figure"); None @@ -5192,7 +5214,7 @@ impl FigureColLights { pub fn texture<'a, const N: usize>( &'a self, model: &'a FigureModelEntry, - ) -> &'a ColLights { + ) -> &'a Texture { /* &self.col_lights */ &model.col_lights } @@ -5203,51 +5225,50 @@ impl FigureColLights { /// NOTE: Panics if the vertex range bounds are not in range of the opaque /// model stored in the BoneMeshes parameter. This is part of the /// function contract. - /// - /// NOTE: Panics if the provided mesh is empty. FIXME: do something else pub fn create_figure( &mut self, renderer: &mut Renderer, (tex, tex_size): ColLightInfo, - (opaque, bounds): (Mesh, math::Aabb), - vertex_ranges: [Range; N], - ) -> FigureModelEntry { + (opaque, bounds): (Mesh, math::Aabb), + vertex_range: [Range; N], + ) -> Result, RenderError> { span!(_guard, "create_figure", "FigureColLights::create_figure"); let atlas = &mut self.atlas; let allocation = atlas - .allocate(guillotiere::Size::new(tex_size.x as i32, tex_size.y as i32)) + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) .expect("Not yet implemented: allocate new atlas on allocation failure."); - let col_lights = pipelines::shadow::create_col_lights(renderer, &(tex, tex_size)); - let col_lights = renderer.figure_bind_col_light(col_lights); + let col_lights = ShadowPipeline::create_col_lights(renderer, &(tex, tex_size))?; let model_len = u32::try_from(opaque.vertices().len()) .expect("The model size for this figure does not fit in a u32!"); - let model = renderer - .create_model(&opaque) - .expect("The model contains no vertices!"); + let model = renderer.create_model(&opaque)?; - vertex_ranges.iter().for_each(|range| { - assert!( - range.start <= range.end && range.end <= model_len, - "The provided vertex range for figure mesh {:?} does not fit in the model, which \ - is of size {:?}!", - range, - model_len - ); - }); - - FigureModelEntry { + Ok(FigureModelEntry { _bounds: bounds, - allocation, + models: vertex_range.map(|range| { + assert!( + range.start <= range.end && range.end <= model_len, + "The provided vertex range for figure mesh {:?} does not fit in the model, \ + which is of size {:?}!", + range, + model_len + ); + FigureModel { + opaque: model.submodel(range), + } + }), col_lights, - lod_vertex_ranges: vertex_ranges, - model: FigureModel { opaque: model }, - } + allocation, + }) } #[allow(clippy::unnecessary_wraps)] fn make_atlas(renderer: &mut Renderer) -> Result { let max_texture_size = renderer.max_texture_size(); - let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { // TODO: Verify some good empirical constants. small_size_threshold: 32, @@ -5280,6 +5301,8 @@ impl FigureColLights { } pub struct FigureStateMeta { + bone_consts: Consts, + locals: Consts, lantern_offset: anim::vek::Vec3, state_time: f32, last_ori: anim::vek::Quaternion, @@ -5291,7 +5314,6 @@ pub struct FigureStateMeta { last_light: f32, last_glow: (Vec3, f32), acc_vel: f32, - bound: pipelines::figure::BoundLocals, } impl FigureStateMeta { @@ -5326,6 +5348,8 @@ impl FigureState { let bone_consts = figure_bone_data_from_anim(&buf); Self { meta: FigureStateMeta { + bone_consts: renderer.create_consts(bone_consts).unwrap(), + locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), lantern_offset, state_time: 0.0, last_ori: Ori::default().into(), @@ -5337,7 +5361,6 @@ impl FigureState { last_light: 1.0, last_glow: (Vec3::zero(), 0.0), acc_vel: 0.0, - bound: renderer.create_figure_bound_locals(&[FigureLocals::default()], bone_consts), }, skeleton, } @@ -5449,13 +5472,18 @@ impl FigureState { self.last_light, self.last_glow, ); - renderer.update_consts(&mut self.meta.bound.0, &[locals]); + renderer.update_consts(&mut self.locals, &[locals]).unwrap(); let lantern_offset = anim::compute_matrices(&self.skeleton, mat, buf); let new_bone_consts = figure_bone_data_from_anim(buf); - renderer.update_consts(&mut self.meta.bound.1, &new_bone_consts[0..S::BONE_COUNT]); + renderer + .update_consts( + &mut self.meta.bone_consts, + &new_bone_consts[0..S::BONE_COUNT], + ) + .unwrap(); self.lantern_offset = lantern_offset; let smoothing = (5.0 * dt).min(1.0); @@ -5472,7 +5500,9 @@ impl FigureState { } } - pub fn bound(&self) -> &pipelines::figure::BoundLocals { &self.bound } + pub fn locals(&self) -> &Consts { &self.locals } + + pub fn bone_consts(&self) -> &Consts { &self.bone_consts } pub fn skeleton_mut(&mut self) -> &mut S { &mut self.skeleton } } @@ -5480,5 +5510,5 @@ impl FigureState { fn figure_bone_data_from_anim( mats: &[anim::FigureBoneData; anim::MAX_BONE_COUNT], ) -> &[FigureBoneData] { - bytemuck::cast_slice(mats) + gfx::memory::cast_slice(mats) } diff --git a/voxygen/src/scene/lod.rs b/voxygen/src/scene/lod.rs index 2e31737c08..76dab1b870 100644 --- a/voxygen/src/scene/lod.rs +++ b/voxygen/src/scene/lod.rs @@ -1,7 +1,7 @@ use crate::{ render::{ - pipelines::lod_terrain::{LodData, Vertex}, - FirstPassDrawer, LodTerrainVertex, Mesh, Model, Quad, Renderer, + pipelines::lod_terrain::{Locals, LodData, Vertex}, + Consts, GlobalModel, LodTerrainPipeline, Mesh, Model, Quad, Renderer, }, settings::Settings, }; @@ -10,7 +10,8 @@ use common::{spiral::Spiral2d, util::srgba_to_linear}; use vek::*; pub struct Lod { - model: Option<(u32, Model)>, + model: Option<(u32, Model)>, + locals: Consts, data: LodData, } @@ -24,15 +25,15 @@ impl Lod { pub fn new(renderer: &mut Renderer, client: &Client, settings: &Settings) -> Self { Self { model: None, + locals: renderer.create_consts(&[Locals::default()]).unwrap(), data: LodData::new( renderer, - client.world_data().chunk_size().as_(), + client.world_data().chunk_size(), client.world_data().lod_base.raw(), client.world_data().lod_alt.raw(), client.world_data().lod_horizon.raw(), settings.graphics.lod_detail.max(100).min(2500), - /* TODO: figure out how we want to do this without color borders? - * water_color().into_array().into(), */ + water_color().into_array().into(), ), } } @@ -60,14 +61,14 @@ impl Lod { } } - pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) { + pub fn render(&self, renderer: &mut Renderer, global: &GlobalModel) { if let Some((_, model)) = self.model.as_ref() { - drawer.draw_lod_terrain(&model); + renderer.render_lod_terrain(&model, global, &self.locals, &self.data); } } } -fn create_lod_terrain_mesh(detail: u32) -> Mesh { +fn create_lod_terrain_mesh(detail: u32) -> Mesh { // detail is even, so we choose odd detail (detail + 1) to create two even // halves with an empty hole. let detail = detail + 1; diff --git a/voxygen/src/scene/math.rs b/voxygen/src/scene/math.rs index dcf55404e7..dcd43c4efd 100644 --- a/voxygen/src/scene/math.rs +++ b/voxygen/src/scene/math.rs @@ -80,7 +80,7 @@ pub fn calc_view_frustum_world_coord>( inv_proj_view: Mat4, ) -> [Vec3; 8] { let mut world_pts = aabb_to_points(Aabb { - min: Vec3::new(-T::one(), -T::one(), T::zero()), + min: -Vec3::one(), max: Vec3::one(), }); mat_mul_points(inv_proj_view, &mut world_pts, |p| Vec3::from(p) / p.w); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 50ef89f52f..daff82a064 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,5 +1,4 @@ pub mod camera; -pub mod debug; pub mod figure; pub mod lod; pub mod math; @@ -9,7 +8,6 @@ pub mod terrain; pub use self::{ camera::{Camera, CameraMode}, - debug::{Debug, DebugShape, DebugShapeId}, figure::FigureMgr, lod::Lod, particle::ParticleMgr, @@ -18,9 +16,9 @@ pub use self::{ use crate::{ audio::{ambient::AmbientMgr, music::MusicMgr, sfx::SfxMgr, AudioFrontend}, render::{ - create_skybox_mesh, CloudsLocals, Consts, Drawer, GlobalModel, Globals, GlobalsBindGroup, - Light, Model, PointLightMatrix, PostProcessLocals, Renderer, Shadow, ShadowLocals, - SkyboxVertex, + create_clouds_mesh, create_pp_mesh, create_skybox_mesh, CloudsLocals, CloudsPipeline, + Consts, GlobalModel, Globals, Light, LodData, Model, PostProcessLocals, + PostProcessPipeline, Renderer, Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, }, settings::Settings, window::{AnalogGameInput, Event}, @@ -36,7 +34,6 @@ use common::{ use common_base::span; use common_state::State; use comp::item::Reagent; -use hashbrown::HashMap; use num::traits::{Float, FloatConst}; use specs::{Entity as EcsEntity, Join, WorldExt}; use vek::*; @@ -44,7 +41,7 @@ use vek::*; // TODO: Don't hard-code this. const CURSOR_PAN_SCALE: f32 = 0.005; -const MAX_LIGHT_COUNT: usize = 20; // 31 (total shadow_mats is limited to 128 with default max_uniform_buffer_binding_size) +const MAX_LIGHT_COUNT: usize = 31; const MAX_SHADOW_COUNT: usize = 24; const NUM_DIRECTED_LIGHTS: usize = 1; const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin @@ -70,19 +67,30 @@ struct EventLight { } struct Skybox { - model: Model, + model: Model, + locals: Consts, +} + +struct Clouds { + model: Model, + locals: Consts, +} + +struct PostProcess { + model: Model, + locals: Consts, } pub struct Scene { data: GlobalModel, - globals_bind_group: GlobalsBindGroup, camera: Camera, camera_input_state: Vec2, event_lights: Vec, skybox: Skybox, + clouds: Clouds, + postprocess: PostProcess, terrain: Terrain, - pub debug: Debug, pub lod: Lod, loaded_distance: f32, /// x coordinate is sea level (minimum height for any land chunk), and y @@ -133,7 +141,7 @@ impl<'a> SceneData<'a> { /// W_e = 2 is the width of the image plane (for our projections, since they go /// from -1 to 1) n_e = near_plane is the near plane for the view frustum /// θ = (fov / 2) is the half-angle of the FOV (the one passed to -/// Mat4::projection_rh_zo). +/// Mat4::projection_rh_no). /// /// Although the widths for the x and y image planes are the same, they are /// different in this framework due to the introduction of an aspect ratio: @@ -265,36 +273,42 @@ impl Scene { client: &Client, settings: &Settings, ) -> Self { - let resolution = renderer.resolution().map(|e| e as f32); + let resolution = renderer.get_resolution().map(|e| e as f32); let sprite_render_context = lazy_init(renderer); - let data = GlobalModel { - globals: renderer.create_consts(&[Globals::default()]), - lights: renderer.create_consts(&[Light::default(); MAX_LIGHT_COUNT]), - shadows: renderer.create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]), - shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]), - point_light_matrices: Box::new([PointLightMatrix::default(); MAX_LIGHT_COUNT * 6 + 6]), - }; - - let lod = Lod::new(renderer, client, settings); - - let globals_bind_group = renderer.bind_globals(&data, lod.get_data()); - - let terrain = Terrain::new(renderer, &data, lod.get_data(), sprite_render_context); - Self { - data, - globals_bind_group, + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer + .create_consts(&[Light::default(); MAX_LIGHT_COUNT]) + .unwrap(), + shadows: renderer + .create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]) + .unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); MAX_LIGHT_COUNT * 6 + 6]) + .unwrap(), + }, camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson), camera_input_state: Vec2::zero(), event_lights: Vec::new(), skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), + locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(), }, - terrain, - debug: Debug::new(), - lod, + clouds: Clouds { + model: renderer.create_model(&create_clouds_mesh()).unwrap(), + locals: renderer.create_consts(&[CloudsLocals::default()]).unwrap(), + }, + postprocess: PostProcess { + model: renderer.create_model(&create_pp_mesh()).unwrap(), + locals: renderer + .create_consts(&[PostProcessLocals::default()]) + .unwrap(), + }, + terrain: Terrain::new(renderer, sprite_render_context), + lod: Lod::new(renderer, client, settings), loaded_distance: 0.0, map_bounds: Vec2::new( client.world_data().min_chunk_alt(), @@ -576,7 +590,9 @@ impl Scene { ); lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32); lights.truncate(MAX_LIGHT_COUNT); - renderer.update_consts(&mut self.data.lights, &lights); + renderer + .update_consts(&mut self.data.lights, &lights) + .expect("Failed to update light constants"); // Update event lights let dt = ecs.fetch::().0; @@ -613,7 +629,9 @@ impl Scene { .collect::>(); shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32); shadows.truncate(MAX_SHADOW_COUNT); - renderer.update_consts(&mut self.data.shadows, &shadows); + renderer + .update_consts(&mut self.data.shadows, &shadows) + .expect("Failed to update light constants"); // Remember to put the new loaded distance back in the scene. self.loaded_distance = loaded_distance; @@ -624,50 +642,60 @@ impl Scene { let focus_off = focus_pos.map(|e| e.trunc()); // Update global constants. - renderer.update_consts(&mut self.data.globals, &[Globals::new( - view_mat, - proj_mat, - cam_pos, - focus_pos, - self.loaded_distance, - self.lod.get_data().tgt_detail as f32, - self.map_bounds, - time_of_day, - scene_data.state.get_time(), - renderer.resolution().as_(), - Vec2::new(SHADOW_NEAR, SHADOW_FAR), - lights.len(), - shadows.len(), - NUM_DIRECTED_LIGHTS, - scene_data - .state - .terrain() - .get((cam_pos + focus_off).map(|e| e.floor() as i32)) - .map(|b| b.kind()) - .unwrap_or(BlockKind::Air), - self.select_pos.map(|e| e - focus_off.map(|e| e as i32)), - scene_data.gamma, - scene_data.exposure, - scene_data.ambiance, - self.camera.get_mode(), - scene_data.sprite_render_distance as f32 - 20.0, - )]); - renderer.update_clouds_locals(CloudsLocals::new(proj_mat_inv, view_mat_inv)); - renderer.update_postprocess_locals(PostProcessLocals::new(proj_mat_inv, view_mat_inv)); + renderer + .update_consts(&mut self.data.globals, &[Globals::new( + view_mat, + proj_mat, + cam_pos, + focus_pos, + self.loaded_distance, + self.lod.get_data().tgt_detail as f32, + self.map_bounds, + time_of_day, + scene_data.state.get_time(), + renderer.get_resolution(), + Vec2::new(SHADOW_NEAR, SHADOW_FAR), + lights.len(), + shadows.len(), + NUM_DIRECTED_LIGHTS, + scene_data + .state + .terrain() + .get((cam_pos + focus_off).map(|e| e.floor() as i32)) + .map(|b| b.kind()) + .unwrap_or(BlockKind::Air), + self.select_pos.map(|e| e - focus_off.map(|e| e as i32)), + scene_data.gamma, + scene_data.exposure, + scene_data.ambiance, + self.camera.get_mode(), + scene_data.sprite_render_distance as f32 - 20.0, + )]) + .expect("Failed to update global constants"); + renderer + .update_consts(&mut self.clouds.locals, &[CloudsLocals::new( + proj_mat_inv, + view_mat_inv, + )]) + .expect("Failed to update cloud locals"); + renderer + .update_consts(&mut self.postprocess.locals, &[PostProcessLocals::new( + proj_mat_inv, + view_mat_inv, + )]) + .expect("Failed to update post-process locals"); // Maintain LoD. self.lod.maintain(renderer); - // Maintain debug shapes - self.debug.maintain(renderer); - // Maintain the terrain. let (_visible_bounds, visible_light_volume, visible_psr_bounds) = self.terrain.maintain( renderer, &scene_data, focus_pos, self.loaded_distance, - &self.camera, + view_mat, + proj_mat, ); // Maintain the figures. @@ -704,11 +732,7 @@ impl Scene { // to transform it correctly into texture coordinates, as well as // OpenGL coordinates. Note that the matrix for directional light // is *already* linear in the depth buffer. - // - // Also, observe that we flip the texture sampling matrix in order to account - // for the fact that DirectX renders top-down. - let texture_mat = Mat4::::scaling_3d::>(Vec3::new(0.5, -0.5, 1.0)) - * Mat4::translation_3d(Vec3::new(1.0, -1.0, 0.0)); + let texture_mat = Mat4::scaling_3d(0.5f32) * Mat4::translation_3d(1.0f32); // We need to compute these offset matrices to transform world space coordinates // to the translated ones we use when multiplying by the light space // matrix; this helps avoid precision loss during the @@ -720,281 +744,226 @@ impl Scene { let new_dir = math::Vec3::from(view_dir); let new_dir = new_dir.normalized(); let up: math::Vec3 = math::Vec3::unit_y(); - let light_view_mat = math::Mat4::look_at_rh(look_at, look_at + directed_light_dir, up); - { - // NOTE: Light view space, right-handed. - let v_p_orig = - math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir)); - let mut v_p = v_p_orig.normalized(); - let cos_gamma = new_dir - .map(f64::from) - .dot(directed_light_dir.map(f64::from)); - let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt(); - let gamma = sin_gamma.asin(); - let view_mat = math::Mat4::from_col_array(view_mat.into_col_array()); - // coordinates are transformed from world space (right-handed) to view space - // (right-handed). - let bounds1 = math::fit_psr( - view_mat.map_cols(math::Vec4::from), - visible_light_volume.iter().copied(), - math::Vec4::homogenized, - ); - let n_e = f64::from(-bounds1.max.z); - let factor = compute_warping_parameter_perspective( - gamma, - n_e, - f64::from(fov), - f64::from(aspect_ratio), - ); - - v_p.z = 0.0; - v_p.normalize(); - let l_r: math::Mat4 = if factor > EPSILON_UPSILON { - // NOTE: Our coordinates are now in left-handed space, but v_p isn't; however, - // v_p has no z component, so we don't have to adjust it for left-handed - // spaces. - math::Mat4::look_at_lh(math::Vec3::zero(), math::Vec3::unit_z(), v_p) - } else { - math::Mat4::identity() - }; - // Convert from right-handed to left-handed coordinates. - let directed_proj_mat = math::Mat4::new( - 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, - ); - - let light_all_mat = l_r * directed_proj_mat * light_view_mat; - // coordinates are transformed from world space (right-handed) to rotated light - // space (left-handed). - let bounds0 = math::fit_psr( - light_all_mat, - visible_light_volume.iter().copied(), - math::Vec4::homogenized, - ); - // Vague idea: project z_n from the camera view to the light view (where it's - // tilted by γ). - // - // NOTE: To transform a normal by M, we multiply by the transpose of the inverse - // of M. For the cases below, we are transforming by an - // already-inverted matrix, so the transpose of its inverse is - // just the transpose of the original matrix. - let (z_0, z_1) = { - let f_e = f64::from(-bounds1.min.z).max(n_e); - // view space, right-handed coordinates. - let p_z = bounds1.max.z; - // rotated light space, left-handed coordinates. - let p_y = bounds0.min.y; - let p_x = bounds0.center().x; - // moves from view-space (right-handed) to world space (right-handed) - let view_inv = view_mat.inverted(); - // moves from rotated light space (left-handed) to world space (right-handed). - let light_all_inv = light_all_mat.inverted(); - - // moves from view-space (right-handed) to world-space (right-handed). - let view_point = view_inv - * math::Vec4::from_point( - -math::Vec3::unit_z() * p_z, /* + math::Vec4::unit_w() */ - ); - let view_plane = view_mat.transposed() * -math::Vec4::unit_z(); - - // moves from rotated light space (left-handed) to world space (right-handed). - let light_point = light_all_inv - * math::Vec4::from_point( - math::Vec3::unit_y() * p_y, /* + math::Vec4::unit_w() */ - ); - let light_plane = light_all_mat.transposed() * math::Vec4::unit_y(); - - // moves from rotated light space (left-handed) to world space (right-handed). - let shadow_point = light_all_inv - * math::Vec4::from_point( - math::Vec3::unit_x() * p_x, /* + math::Vec4::unit_w() */ - ); - let shadow_plane = light_all_mat.transposed() * math::Vec4::unit_x(); - - // Find the point at the intersection of the three planes; note that since the - // equations are already in right-handed world space, we don't need to negate - // the z coordinates. - let solve_p0 = math::Mat4::new( - view_plane.x, - view_plane.y, - view_plane.z, - 0.0, - light_plane.x, - light_plane.y, - light_plane.z, - 0.0, - shadow_plane.x, - shadow_plane.y, - shadow_plane.z, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - ); - - // in world-space (right-handed). - let plane_dist = math::Vec4::new( - view_plane.dot(view_point), - light_plane.dot(light_point), - shadow_plane.dot(shadow_point), - 1.0, - ); - let p0_world = solve_p0.inverted() * plane_dist; - // in rotated light-space (left-handed). - let p0 = light_all_mat * p0_world; - let mut p1 = p0; - // in rotated light-space (left-handed). - p1.y = bounds0.max.y; - - // transforms from rotated light-space (left-handed) to view space - // (right-handed). - let view_from_light_mat = view_mat * light_all_inv; - // z0 and z1 are in view space (right-handed). - let z0 = view_from_light_mat * p0; - let z1 = view_from_light_mat * p1; - - // Extract the homogenized forward component (right-handed). - // - // NOTE: I don't think the w component should be anything but 1 here, but - // better safe than sorry. - ( - f64::from(z0.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e), - f64::from(z1.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e), - ) - }; - - // all of this is in rotated light-space (left-handed). - let mut light_focus_pos: math::Vec3 = math::Vec3::zero(); - light_focus_pos.x = bounds0.center().x; - light_focus_pos.y = bounds0.min.y; - light_focus_pos.z = bounds0.center().z; - - let d = f64::from(bounds0.max.y - bounds0.min.y).abs(); - - let w_l_y = d; - - // NOTE: See section 5.1.2.2 of Lloyd's thesis. - // NOTE: Since z_1 and z_0 are in the same coordinate space, we don't have to - // worry about the handedness of their ratio. - let alpha = z_1 / z_0; - let alpha_sqrt = alpha.sqrt(); - let directed_near_normal = if factor < 0.0 { - // Standard shadow map to LiSPSM - (1.0 + alpha_sqrt - factor * (alpha - 1.0)) / ((alpha - 1.0) * (factor + 1.0)) - } else { - // LiSPSM to PSM - ((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip() - }; - - // Equation 5.14 - 5.16 - let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs(); - let directed_near = y_(0.0) as f32; - let directed_far = y_(1.0) as f32; - light_focus_pos.y = if factor > EPSILON_UPSILON { - light_focus_pos.y - directed_near - } else { - light_focus_pos.y - }; - // Left-handed translation. - let w_v: math::Mat4 = math::Mat4::translation_3d(-math::Vec3::new( - light_focus_pos.x, - light_focus_pos.y, - light_focus_pos.z, - )); - let shadow_view_mat: math::Mat4 = w_v * light_all_mat; - let w_p: math::Mat4 = { - if factor > EPSILON_UPSILON { - // Projection for y - let near = directed_near; - let far = directed_far; - let left = -1.0; - let right = 1.0; - let bottom = -1.0; - let top = 1.0; - let s_x = 2.0 * near / (right - left); - let o_x = (right + left) / (right - left); - let s_z = 2.0 * near / (top - bottom); - let o_z = (top + bottom) / (top - bottom); - - let s_y = (far + near) / (far - near); - let o_y = -2.0 * far * near / (far - near); - - math::Mat4::new( - s_x, o_x, 0.0, 0.0, 0.0, s_y, 0.0, o_y, 0.0, o_z, s_z, 0.0, 0.0, 1.0, - 0.0, 0.0, - ) - } else { - math::Mat4::identity() - } - }; - - let shadow_all_mat: math::Mat4 = w_p * shadow_view_mat; - // coordinates are transformed from world space (right-handed) - // to post-warp light space (left-handed), then homogenized. - let math::Aabb:: { - min: - math::Vec3 { - x: xmin, - y: ymin, - z: zmin, - }, - max: - math::Vec3 { - x: xmax, - y: ymax, - z: zmax, - }, - } = math::fit_psr( - shadow_all_mat, - visible_light_volume.iter().copied(), - math::Vec4::homogenized, - ); - let s_x = 2.0 / (xmax - xmin); - let s_y = 2.0 / (ymax - ymin); - let s_z = 1.0 / (zmax - zmin); - let o_x = -(xmax + xmin) / (xmax - xmin); - let o_y = -(ymax + ymin) / (ymax - ymin); - let o_z = -zmin / (zmax - zmin); - let directed_proj_mat = Mat4::new( - s_x, 0.0, 0.0, o_x, 0.0, s_y, 0.0, o_y, 0.0, 0.0, s_z, o_z, 0.0, 0.0, 0.0, 1.0, - ); - - let shadow_all_mat: Mat4 = - Mat4::from_col_arrays(shadow_all_mat.into_col_arrays()); - - let directed_texture_proj_mat = texture_mat * directed_proj_mat; - let shadow_locals = ShadowLocals::new( - directed_proj_mat * shadow_all_mat, - directed_texture_proj_mat * shadow_all_mat, - ); - - renderer.update_consts(&mut self.data.shadow_mats, &[shadow_locals]); - } - directed_shadow_mats.push(light_view_mat); + directed_shadow_mats.push(math::Mat4::look_at_rh( + look_at, + look_at + directed_light_dir, + up, + )); // This leaves us with five dummy slots, which we push as defaults. directed_shadow_mats .extend_from_slice(&[math::Mat4::default(); 6 - NUM_DIRECTED_LIGHTS] as _); // Now, construct the full projection matrices in the first two directed light // slots. let mut shadow_mats = Vec::with_capacity(6 * (lights.len() + 1)); - shadow_mats.resize_with(6, PointLightMatrix::default); + shadow_mats.extend(directed_shadow_mats.iter().enumerate().map( + move |(idx, &light_view_mat)| { + if idx >= NUM_DIRECTED_LIGHTS { + return ShadowLocals::new(Mat4::identity(), Mat4::identity()); + } + + let v_p_orig = + math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir)); + let mut v_p = v_p_orig.normalized(); + let cos_gamma = new_dir + .map(f64::from) + .dot(directed_light_dir.map(f64::from)); + let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt(); + let gamma = sin_gamma.asin(); + let view_mat = math::Mat4::from_col_array(view_mat.into_col_array()); + let bounds1 = math::fit_psr( + view_mat.map_cols(math::Vec4::from), + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let n_e = f64::from(-bounds1.max.z); + let factor = compute_warping_parameter_perspective( + gamma, + n_e, + f64::from(fov), + f64::from(aspect_ratio), + ); + + v_p.z = 0.0; + v_p.normalize(); + let l_r: math::Mat4 = if factor > EPSILON_UPSILON { + math::Mat4::look_at_rh(math::Vec3::zero(), -math::Vec3::unit_z(), v_p) + } else { + math::Mat4::identity() + }; + let directed_proj_mat = math::Mat4::new( + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, + 1.0, + ); + + let light_all_mat = l_r * directed_proj_mat * light_view_mat; + let bounds0 = math::fit_psr( + light_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + // Vague idea: project z_n from the camera view to the light view (where it's + // tilted by γ). + let (z_0, z_1) = { + let p_z = bounds1.max.z; + let p_y = bounds0.min.y; + let p_x = bounds0.center().x; + let view_inv = view_mat.inverted(); + let light_all_inv = light_all_mat.inverted(); + + let view_point = view_inv * math::Vec4::new(0.0, 0.0, p_z, 1.0); + let view_plane = + view_inv * math::Vec4::from_direction(math::Vec3::unit_z()); + + let light_point = light_all_inv * math::Vec4::new(0.0, p_y, 0.0, 1.0); + let light_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_y()); + + let shadow_point = light_all_inv * math::Vec4::new(p_x, 0.0, 0.0, 1.0); + let shadow_plane = + light_all_inv * math::Vec4::from_direction(math::Vec3::unit_x()); + + let solve_p0 = math::Mat4::new( + view_plane.x, + view_plane.y, + view_plane.z, + -view_plane.dot(view_point), + light_plane.x, + light_plane.y, + light_plane.z, + -light_plane.dot(light_point), + shadow_plane.x, + shadow_plane.y, + shadow_plane.z, + -shadow_plane.dot(shadow_point), + 0.0, + 0.0, + 0.0, + 1.0, + ); + + let p0_world = solve_p0.inverted() * math::Vec4::unit_w(); + let p0 = light_all_mat * p0_world; + let mut p1 = p0; + p1.y = bounds0.max.y; + + let view_from_light_mat = view_mat * light_all_inv; + let z0 = view_from_light_mat * p0; + let z1 = view_from_light_mat * p1; + + (f64::from(z0.z), f64::from(z1.z)) + }; + + let mut light_focus_pos: math::Vec3 = math::Vec3::zero(); + light_focus_pos.x = bounds0.center().x; + light_focus_pos.y = bounds0.min.y; + light_focus_pos.z = bounds0.center().z; + + let d = f64::from(bounds0.max.y - bounds0.min.y).abs(); + + let w_l_y = d; + + // NOTE: See section 5.1.2.2 of Lloyd's thesis. + let alpha = z_1 / z_0; + let alpha_sqrt = alpha.sqrt(); + let directed_near_normal = if factor < 0.0 { + // Standard shadow map to LiSPSM + (1.0 + alpha_sqrt - factor * (alpha - 1.0)) + / ((alpha - 1.0) * (factor + 1.0)) + } else { + // LiSPSM to PSM + ((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip() + }; + + // Equation 5.14 - 5.16 + let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs(); + let directed_near = y_(0.0) as f32; + let directed_far = y_(1.0) as f32; + light_focus_pos.y = if factor > EPSILON_UPSILON { + light_focus_pos.y - directed_near + } else { + light_focus_pos.y + }; + let w_v: math::Mat4 = math::Mat4::translation_3d(-math::Vec3::new( + light_focus_pos.x, + light_focus_pos.y, + light_focus_pos.z, + )); + let shadow_view_mat: math::Mat4 = w_v * light_all_mat; + let w_p: math::Mat4 = { + if factor > EPSILON_UPSILON { + // Projection for y + let near = directed_near; + let far = directed_far; + let left = -1.0; + let right = 1.0; + let bottom = -1.0; + let top = 1.0; + let s_x = 2.0 * near / (right - left); + let o_x = (right + left) / (right - left); + let s_z = 2.0 * near / (top - bottom); + let o_z = (top + bottom) / (top - bottom); + + let s_y = (far + near) / (far - near); + let o_y = -2.0 * far * near / (far - near); + + math::Mat4::new( + s_x, o_x, 0.0, 0.0, 0.0, s_y, 0.0, o_y, 0.0, o_z, s_z, 0.0, 0.0, + 1.0, 0.0, 0.0, + ) + } else { + math::Mat4::identity() + } + }; + + let shadow_all_mat: math::Mat4 = w_p * shadow_view_mat; + let math::Aabb:: { + min: + math::Vec3 { + x: xmin, + y: ymin, + z: zmin, + }, + max: + math::Vec3 { + x: xmax, + y: ymax, + z: zmax, + }, + } = math::fit_psr( + shadow_all_mat, + visible_light_volume.iter().copied(), + math::Vec4::homogenized, + ); + let s_x = 2.0 / (xmax - xmin); + let s_y = 2.0 / (ymax - ymin); + let s_z = 2.0 / (zmax - zmin); + let o_x = -(xmax + xmin) / (xmax - xmin); + let o_y = -(ymax + ymin) / (ymax - ymin); + let o_z = -(zmax + zmin) / (zmax - zmin); + let directed_proj_mat = Mat4::new( + s_x, 0.0, 0.0, o_x, 0.0, s_y, 0.0, o_y, 0.0, 0.0, s_z, o_z, 0.0, 0.0, 0.0, + 1.0, + ); + + let shadow_all_mat: Mat4 = + Mat4::from_col_arrays(shadow_all_mat.into_col_arrays()); + + let directed_texture_proj_mat = texture_mat * directed_proj_mat; + ShadowLocals::new( + directed_proj_mat * shadow_all_mat, + directed_texture_proj_mat * shadow_all_mat, + ) + }, + )); // Now, we tackle point lights. // First, create a perspective projection matrix at 90 degrees (to cover a whole - // face of the cube map we're using); we use a negative near plane to exactly - // match OpenGL's behavior if we use a left-handed coordinate system everywhere - // else. - let shadow_proj = camera::perspective_rh_zo_general( + // face of the cube map we're using). + let shadow_proj = Mat4::perspective_rh_no( 90.0f32.to_radians(), point_shadow_aspect, - 1.0 / SHADOW_NEAR, - 1.0 / SHADOW_FAR, + SHADOW_NEAR, + SHADOW_FAR, ); - // NOTE: We negate here to emulate a right-handed projection with a negative - // near plane, which produces the correct transformation to exactly match - // OpenGL's rendering behavior if we use a left-handed coordinate - // system everywhere else. - let shadow_proj = shadow_proj * Mat4::scaling_3d(-1.0); - // Next, construct the 6 orientations we'll use for the six faces, in terms of // their (forward, up) vectors. let orientations = [ @@ -1005,7 +974,6 @@ impl Scene { (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, -1.0, 0.0)), (Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, -1.0, 0.0)), ]; - // NOTE: We could create the shadow map collection at the same time as the // lights, but then we'd have to sort them both, which wastes time. Plus, we // want to prepend our directed lights. @@ -1016,13 +984,16 @@ impl Scene { orientations.iter().map(move |&(forward, up)| { // NOTE: We don't currently try to linearize point lights or need a separate // transform for them. - PointLightMatrix::new(shadow_proj * Mat4::look_at_lh(eye, eye + forward, up)) + ShadowLocals::new( + shadow_proj * Mat4::look_at_rh(eye, eye + forward, up), + Mat4::identity(), + ) }) })); - for (i, val) in shadow_mats.into_iter().enumerate() { - self.data.point_light_matrices[i] = val - } + renderer + .update_consts(&mut self.data.shadow_mats, &shadow_mats) + .expect("Failed to update light constants"); } // Remove unused figures. @@ -1042,12 +1013,10 @@ impl Scene { .maintain(audio, scene_data.state, client, &self.camera); } - pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group } - - /// Render the scene using the provided `Drawer`. - pub fn render<'a>( - &'a self, - drawer: &mut Drawer<'a>, + /// Render the scene using the provided `Renderer`. + pub fn render( + &mut self, + renderer: &mut Renderer, state: &State, player_entity: EcsEntity, tick: u64, @@ -1059,120 +1028,84 @@ impl Scene { let focus_pos = self.camera.get_focus_pos(); let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); + let global = &self.data; + let light_data = (is_daylight, &*self.light_data); let camera_data = (&self.camera, scene_data.figure_lod_render_distance); // would instead have this as an extension. - if drawer.render_mode().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { + if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) { if is_daylight { - if let Some(mut shadow_pass) = drawer.shadow_pass() { - // Render terrain directed shadows. - self.terrain - .render_shadows(&mut shadow_pass.draw_terrain_shadows(), focus_pos); - - // Render figure directed shadows. - self.figure_mgr.render_shadows( - &mut shadow_pass.draw_figure_shadows(), - state, - tick, - camera_data, - ); - } + // Set up shadow mapping. + renderer.start_shadows(); } - // Render terrain point light shadows. - drawer.draw_point_shadows( - &self.data.point_light_matrices, - self.terrain.chunks_for_point_shadows(focus_pos), - ) - } + // Render terrain shadows. + self.terrain + .render_shadows(renderer, global, light_data, focus_pos); - if let Some(mut first_pass) = drawer.first_pass() { - self.figure_mgr.render_player( - &mut first_pass.draw_figures(), - state, - player_entity, - tick, - camera_data, - ); + // Render figure shadows. + self.figure_mgr + .render_shadows(renderer, state, tick, global, light_data, camera_data); - self.terrain.render(&mut first_pass, focus_pos); - - self.figure_mgr.render( - &mut first_pass.draw_figures(), - state, - player_entity, - tick, - camera_data, - ); - - self.lod.render(&mut first_pass); - - // Render the skybox. - first_pass.draw_skybox(&self.skybox.model); - - // Draws translucent terrain and sprites - self.terrain.render_translucent( - &mut first_pass, - focus_pos, - cam_pos, - scene_data.sprite_render_distance, - ); - - // Render particle effects. - self.particle_mgr - .render(&mut first_pass.draw_particles(), scene_data); - - // Render debug shapes - self.debug.render(&mut first_pass.draw_debug()); - } - } - - pub fn maintain_debug_hitboxes( - &mut self, - client: &Client, - settings: &Settings, - hitboxes: &mut HashMap, - ) { - let ecs = client.state().ecs(); - let mut current_entities = hashbrown::HashSet::new(); - if settings.interface.toggle_hitboxes { - let positions = ecs.read_component::(); - let colliders = ecs.read_component::(); - let groups = ecs.read_component::(); - for (entity, pos, collider, group) in - (&ecs.entities(), &positions, &colliders, groups.maybe()).join() - { - if let comp::Collider::Box { - radius, - z_min, - z_max, - } = collider - { - current_entities.insert(entity); - let shape_id = hitboxes.entry(entity).or_insert_with(|| { - self.debug.add_shape(DebugShape::Cylinder { - radius: *radius, - height: *z_max - *z_min, - }) - }); - let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0]; - let color = if group == Some(&comp::group::ENEMY) { - [1.0, 0.0, 0.0, 0.5] - } else if group == Some(&comp::group::NPC) { - [0.0, 0.0, 1.0, 0.5] - } else { - [0.0, 1.0, 0.0, 0.5] - }; - self.debug.set_pos_and_color(*shape_id, hb_pos, color); - } + if is_daylight { + // Flush shadows. + renderer.flush_shadows(); } } - hitboxes.retain(|k, v| { - let keep = current_entities.contains(k); - if !keep { - self.debug.remove_shape(*v); - } - keep - }); + let lod = self.lod.get_data(); + + self.figure_mgr.render_player( + renderer, + state, + player_entity, + tick, + global, + lod, + camera_data, + ); + + // Render terrain and figures. + self.terrain.render(renderer, global, lod, focus_pos); + + self.figure_mgr.render( + renderer, + state, + player_entity, + tick, + global, + lod, + camera_data, + ); + self.lod.render(renderer, global); + + // Render the skybox. + renderer.render_skybox(&self.skybox.model, global, &self.skybox.locals, lod); + + self.terrain.render_translucent( + renderer, + global, + lod, + focus_pos, + cam_pos, + scene_data.sprite_render_distance, + ); + + // Render particle effects. + self.particle_mgr.render(renderer, scene_data, global, lod); + + // Render clouds (a post-processing effect) + renderer.render_clouds( + &self.clouds.model, + &global.globals, + &self.clouds.locals, + self.lod.get_data(), + ); + + renderer.render_post_process( + &self.postprocess.model, + &global.globals, + &self.postprocess.locals, + self.lod.get_data(), + ); } } diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 0bb3297d83..35d6ac8db3 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -1,9 +1,9 @@ use super::{terrain::BlocksOfInterest, SceneData, Terrain}; use crate::{ - mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_particle}, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - pipelines::particle::ParticleMode, Instances, Light, Model, ParticleDrawer, - ParticleInstance, ParticleVertex, Renderer, + pipelines::particle::ParticleMode, GlobalModel, Instances, Light, LodData, Model, + ParticleInstance, ParticlePipeline, Renderer, }, }; use common::{ @@ -38,7 +38,7 @@ pub struct ParticleMgr { instances: Instances, /// GPU Vertex Buffers - model_cache: HashMap<&'static str, Model>, + model_cache: HashMap<&'static str, Model>, } impl ParticleMgr { @@ -276,18 +276,15 @@ impl ParticleMgr { self.maintain_shockwave_particles(scene_data); self.maintain_aura_particles(scene_data); self.maintain_buff_particles(scene_data); - - self.upload_particles(renderer); } else { // remove all particle lifespans - if !self.particles.is_empty() { - self.particles.clear(); - self.upload_particles(renderer); - } + self.particles.clear(); // remove all timings self.scheduler.clear(); } + + self.upload_particles(renderer); } fn maintain_body_particles(&mut self, scene_data: &SceneData) { @@ -1339,7 +1336,13 @@ impl ParticleMgr { self.instances = gpu_instances; } - pub fn render<'a>(&'a self, drawer: &mut ParticleDrawer<'_, 'a>, scene_data: &SceneData) { + pub fn render( + &self, + renderer: &mut Renderer, + scene_data: &SceneData, + global: &GlobalModel, + lod: &LodData, + ) { span!(_guard, "render", "ParticleMgr::render"); if scene_data.particles_enabled { let model = &self @@ -1347,7 +1350,7 @@ impl ParticleMgr { .get(DEFAULT_MODEL_KEY) .expect("Expected particle model in cache"); - drawer.draw(model, &self.instances); + renderer.render_particles(model, global, &self.instances, lod); } } @@ -1366,7 +1369,7 @@ fn default_instances(renderer: &mut Renderer) -> Instances { const DEFAULT_MODEL_KEY: &str = "voxygen.voxel.particle"; -fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model> { +fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model> { let mut model_cache = HashMap::new(); model_cache.entry(DEFAULT_MODEL_KEY).or_insert_with(|| { @@ -1375,12 +1378,14 @@ fn default_cache(renderer: &mut Renderer) -> HashMap<&'static str, Model::generate_mesh(segment, &mut greedy).0; // Center particle vertices around origin for vert in mesh.vertices_mut() { vert.pos[0] -= segment_size.x as f32 / 2.0; diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 54fd63bea4..2b0b0135a9 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -1,13 +1,15 @@ use crate::{ - mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain}, + mesh::{greedy::GreedyMesh, Meshable}, render::{ - create_skybox_mesh, BoneMeshes, Consts, FigureModel, FirstPassDrawer, GlobalModel, Globals, - GlobalsBindGroup, Light, LodData, Mesh, Model, PointLightMatrix, Renderer, Shadow, - ShadowLocals, SkyboxVertex, TerrainVertex, + create_clouds_mesh, create_pp_mesh, create_skybox_mesh, BoneMeshes, CloudsLocals, + CloudsPipeline, Consts, FigureModel, FigurePipeline, GlobalModel, Globals, Light, Mesh, + Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, ShadowLocals, + SkyboxLocals, SkyboxPipeline, TerrainPipeline, }, scene::{ camera::{self, Camera, CameraMode}, figure::{load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState}, + LodData, }, window::{Event, PressState}, }; @@ -28,6 +30,7 @@ use common::{ terrain::BlockKind, vol::{BaseVol, ReadVol}, }; +use tracing::error; use vek::*; use winit::event::MouseButton; @@ -42,26 +45,41 @@ impl ReadVol for VoidVol { fn generate_mesh<'a>( greedy: &mut GreedyMesh<'a>, - mesh: &mut Mesh, + mesh: &mut Mesh, segment: Segment, offset: Vec3, bone_idx: u8, ) -> BoneMeshes { let (opaque, _, /* shadow */ _, bounds) = - generate_mesh_base_vol_terrain(segment, (greedy, mesh, offset, Vec3::one(), bone_idx)); + Meshable::::generate_mesh( + segment, + (greedy, mesh, offset, Vec3::one(), bone_idx), + ); (opaque /* , shadow */, bounds) } struct Skybox { - model: Model, + model: Model, + locals: Consts, +} + +struct PostProcess { + model: Model, + locals: Consts, +} + +struct Clouds { + model: Model, + locals: Consts, } pub struct Scene { data: GlobalModel, - globals_bind_group: GlobalsBindGroup, camera: Camera, skybox: Skybox, + clouds: Clouds, + postprocess: PostProcess, lod: LodData, map_bounds: Vec2, @@ -91,12 +109,16 @@ pub struct SceneData<'a> { impl Scene { pub fn new(renderer: &mut Renderer, backdrop: Option<&str>, client: &Client) -> Self { let start_angle = 90.0f32.to_radians(); - let resolution = renderer.resolution().map(|e| e as f32); + let resolution = renderer.get_resolution().map(|e| e as f32); let map_bounds = Vec2::new( client.world_data().min_chunk_alt(), client.world_data().max_chunk_alt(), ); + let map_border = [0.0, 0.0, 0.0, 0.0]; + let map_image = [0]; + let alt_image = [0]; + let horizon_image = [0x_00_01_00_01]; let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson); camera.set_focus_pos(Vec3::unit_z() * 1.5); @@ -105,24 +127,39 @@ impl Scene { let mut col_lights = FigureColLights::new(renderer); - let data = GlobalModel { - globals: renderer.create_consts(&[Globals::default()]), - lights: renderer.create_consts(&[Light::default(); 20]), - shadows: renderer.create_consts(&[Shadow::default(); 24]), - shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]), - point_light_matrices: Box::new([PointLightMatrix::default(); 126]), - }; - let lod = LodData::dummy(renderer); - - let globals_bind_group = renderer.bind_globals(&data, &lod); - Self { - data, - globals_bind_group, + data: GlobalModel { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), + shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), + shadow_mats: renderer + .create_consts(&[ShadowLocals::default(); 6]) + .unwrap(), + }, + skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), + locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(), }, - lod, + clouds: Clouds { + model: renderer.create_model(&create_clouds_mesh()).unwrap(), + locals: renderer.create_consts(&[CloudsLocals::default()]).unwrap(), + }, + postprocess: PostProcess { + model: renderer.create_model(&create_pp_mesh()).unwrap(), + locals: renderer + .create_consts(&[PostProcessLocals::default()]) + .unwrap(), + }, + lod: LodData::new( + renderer, + Vec2::new(1, 1), + &map_image, + &alt_image, + &horizon_image, + 1, + map_border.into(), + ), map_bounds, figure_model_cache: FigureModelCache::new(), @@ -140,9 +177,9 @@ impl Scene { // total size is bounded by 2^24 * 3 * 1.5 which is bounded by // 2^27, which fits in a u32. let range = 0..opaque_mesh.vertices().len() as u32; - let model = - col_lights - .create_figure(renderer, greedy.finalize(), (opaque_mesh, bounds), [range]); + let model = col_lights + .create_figure(renderer, greedy.finalize(), (opaque_mesh, bounds), [range]) + .unwrap(); let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; state.update( renderer, @@ -240,7 +277,7 @@ impl Scene { const SHADOW_NEAR: f32 = 1.0; const SHADOW_FAR: f32 = 25.0; - renderer.update_consts(&mut self.data.globals, &[Globals::new( + if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, @@ -250,7 +287,7 @@ impl Scene { self.map_bounds, TIME, scene_data.time, - renderer.resolution().as_(), + renderer.get_resolution(), Vec2::new(SHADOW_NEAR, SHADOW_FAR), 0, 0, @@ -262,7 +299,9 @@ impl Scene { scene_data.ambiance, self.camera.get_mode(), 250.0, - )]); + )]) { + error!(?e, "Renderer failed to update"); + } self.figure_model_cache .clean(&mut self.col_lights, scene_data.tick); @@ -345,16 +384,20 @@ impl Scene { } } - pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group } - - pub fn render<'a>( - &'a self, - drawer: &mut FirstPassDrawer<'a>, + pub fn render( + &mut self, + renderer: &mut Renderer, tick: u64, body: Option, inventory: Option<&Inventory>, ) { - let mut figure_drawer = drawer.draw_figures(); + renderer.render_skybox( + &self.skybox.model, + &self.data, + &self.skybox.locals, + &self.lod, + ); + if let Some(body) = body { let model = &self.figure_model_cache.get_model( &self.col_lights, @@ -366,23 +409,40 @@ impl Scene { ); if let Some(model) = model { - figure_drawer.draw( - model.lod_model(0), - self.figure_state.bound(), + renderer.render_figure( + &model.models[0], &self.col_lights.texture(model), + &self.data, + self.figure_state.locals(), + self.figure_state.bone_consts(), + &self.lod, ); } } if let Some((model, state)) = &self.backdrop { - figure_drawer.draw( - model.lod_model(0), - state.bound(), + renderer.render_figure( + &model.models[0], &self.col_lights.texture(model), + &self.data, + state.locals(), + state.bone_consts(), + &self.lod, ); } - drop(figure_drawer); - drawer.draw_skybox(&self.skybox.model); + renderer.render_clouds( + &self.clouds.model, + &self.data.globals, + &self.clouds.locals, + &self.lod, + ); + + renderer.render_post_process( + &self.postprocess.model, + &self.data.globals, + &self.postprocess.locals, + &self.lod, + ); } } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 36495d259c..65e34bde73 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,25 +1,16 @@ mod watcher; - pub use self::watcher::{BlocksOfInterest, Interaction}; use crate::{ - mesh::{ - greedy::GreedyMesh, - segment::generate_mesh_base_vol_sprite, - terrain::{generate_mesh, SUNLIGHT}, - }, + mesh::{greedy::GreedyMesh, terrain::SUNLIGHT, Meshable}, render::{ - pipelines::{self, ColLights}, - ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model, - RenderError, Renderer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, - TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, + ColLightFmt, ColLightInfo, Consts, FluidPipeline, GlobalModel, Instances, Mesh, Model, + RenderError, Renderer, ShadowPipeline, SpriteInstance, SpriteLocals, SpritePipeline, + TerrainLocals, TerrainPipeline, Texture, }, }; -use super::{ - camera::{self, Camera}, - math, SceneData, -}; +use super::{math, LodData, SceneData}; use common::{ assets::{self, AssetExt, DotVoxAsset}, figure::Segment, @@ -44,7 +35,6 @@ use treeculler::{BVol, Frustum, AABB}; use vek::*; const SPRITE_SCALE: Vec3 = Vec3::new(1.0 / 11.0, 1.0 / 11.0, 1.0 / 11.0); -const SPRITE_LOD_LEVELS: usize = 5; #[derive(Clone, Copy, Debug)] struct Visibility { @@ -71,22 +61,22 @@ type LightMapFn = Arc) -> f32 + Send + Sync>; pub struct TerrainChunkData { // GPU data load_time: f32, - opaque_model: Option>, - fluid_model: Option>, + opaque_model: Model, + fluid_model: Option>, /// If this is `None`, this texture is not allocated in the current atlas, /// and therefore there is no need to free its allocation. - col_lights_alloc: Option, + col_lights: Option, /// The actual backing texture for this chunk. Use this for rendering /// purposes. The texture is reference-counted, so it will be /// automatically freed when no chunks are left that need it (though /// shadow chunks will still keep it alive; we could deal with this by /// making this an `Option`, but it probably isn't worth it since they /// shouldn't be that much more nonlocal than regular chunks). - col_lights: Arc>, + texture: Texture, light_map: LightMapFn, glow_map: LightMapFn, - sprite_instances: [Instances; SPRITE_LOD_LEVELS], - locals: pipelines::terrain::BoundLocals, + sprite_instances: HashMap<(SpriteKind, usize), Instances>, + locals: Consts, pub blocks_of_interest: BlocksOfInterest, visible: Visibility, @@ -108,8 +98,8 @@ struct ChunkMeshState { /// Just the mesh part of a mesh worker response. pub struct MeshWorkerResponseMesh { z_bounds: (f32, f32), - opaque_mesh: Mesh, - fluid_mesh: Mesh, + opaque_mesh: Mesh, + fluid_mesh: Mesh, col_lights_info: ColLightInfo, light_map: LightMapFn, glow_map: LightMapFn, @@ -119,7 +109,7 @@ pub struct MeshWorkerResponseMesh { /// mesh of a chunk. struct MeshWorkerResponse { pos: Vec2, - sprite_instances: [Vec; SPRITE_LOD_LEVELS], + sprite_instances: HashMap<(SpriteKind, usize), Vec>, /// If None, this update was requested without meshing. mesh: Option, started_tick: u64, @@ -179,26 +169,22 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug + ' max_texture_size: u16, chunk: Arc, range: Aabb, - sprite_data: &HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>, + sprite_data: &HashMap<(SpriteKind, usize), Vec>, sprite_config: &SpriteSpec, ) -> MeshWorkerResponse { span!(_guard, "mesh_worker"); let blocks_of_interest = BlocksOfInterest::from_chunk(&chunk); - let mesh; let (light_map, glow_map) = if let Some((light_map, glow_map)) = &skip_remesh { mesh = None; (&**light_map, &**glow_map) } else { let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info, light_map, glow_map)) = - generate_mesh( - &volume, - ( - range, - Vec2::new(max_texture_size, max_texture_size), - &blocks_of_interest, - ), - ); + volume.generate_mesh(( + range, + Vec2::new(max_texture_size, max_texture_size), + &blocks_of_interest, + )); mesh = Some(MeshWorkerResponseMesh { // TODO: Take sprite bounds into account somehow? z_bounds: (bounds.min.z, bounds.max.z), @@ -212,13 +198,12 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug + ' let mesh = mesh.as_ref().unwrap(); (&*mesh.light_map, &*mesh.glow_map) }; - MeshWorkerResponse { pos, // Extract sprite locations from volume sprite_instances: { span!(_guard, "extract sprite_instances"); - let mut instances = [(); SPRITE_LOD_LEVELS].map(|()| Vec::new()); + let mut instances = HashMap::new(); for x in 0..V::RECT_SIZE.x as i32 { for y in 0..V::RECT_SIZE.y as i32 { @@ -246,39 +231,23 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug + ' let key = (sprite, variation); // NOTE: Safe because we called sprite_config_for already. // NOTE: Safe because 0 ≤ ori < 8 - let light = light_map(wpos); - let glow = glow_map(wpos); - - for (lod_level, sprite_data) in - instances.iter_mut().zip(&sprite_data[&key]) - { - let mat = Mat4::identity() - // Scaling for different LOD resolutions - .scaled_3d(sprite_data.scale) - // Offset + let sprite_data = &sprite_data[&key][0]; + let instance = SpriteInstance::new( + Mat4::identity() .translated_3d(sprite_data.offset) - .scaled_3d(SPRITE_SCALE) .rotated_z(f32::consts::PI * 0.25 * ori as f32) .translated_3d( - rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0) - ); - // Add an instance for each page in the sprite model - for page in sprite_data.vert_pages.clone() { - // TODO: could be more efficient to create once and clone while - // modifying vert_page - let instance = SpriteInstance::new( - mat, - cfg.wind_sway, - sprite_data.scale.z, - rel_pos, - ori, - light, - glow, - page, - ); - lod_level.push(instance); - } - } + (rel_pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)) + / SPRITE_SCALE, + ), + cfg.wind_sway, + rel_pos, + ori, + light_map(wpos), + glow_map(wpos), + ); + + instances.entry(key).or_insert(Vec::new()).push(instance); } } } @@ -293,11 +262,10 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug + ' } struct SpriteData { - // Sprite vert page ranges that need to be drawn - vert_pages: core::ops::Range, - // Scale - scale: Vec3, - // Offset + /* mat: Mat4, */ + locals: Consts, + model: Model, + /* scale: Vec3, */ offset: Vec3, } @@ -348,15 +316,14 @@ pub struct Terrain { mesh_recv_overflow: f32, // GPU data - // Maps sprite kind + variant to data detailing how to render it - sprite_data: Arc>, - sprite_globals: SpriteGlobalsBindGroup, - sprite_col_lights: Arc>, + sprite_data: Arc>>, + sprite_col_lights: Texture, /// As stated previously, this is always the very latest texture into which /// we allocate. Code cannot assume that this is the assigned texture /// for any particular chunk; look at the `texture` field in /// `TerrainChunkData` for that. - col_lights: Arc>, + col_lights: Texture, + waves: Texture, phantom: PhantomData, } @@ -368,10 +335,8 @@ impl TerrainChunkData { #[derive(Clone)] pub struct SpriteRenderContext { sprite_config: Arc, - // Maps sprite kind + variant to data detailing how to render it - sprite_data: Arc>, - sprite_col_lights: Arc>, - sprite_verts_buffer: Arc, + sprite_data: Arc>>, + sprite_col_lights: Texture, } pub type SpriteRenderContextLazy = Box SpriteRenderContext>; @@ -381,11 +346,16 @@ impl SpriteRenderContext { pub fn new(renderer: &mut Renderer) -> SpriteRenderContextLazy { let max_texture_size = renderer.max_texture_size(); + struct SpriteDataResponse { + locals: [SpriteLocals; 8], + model: Mesh, + offset: Vec3, + } + struct SpriteWorkerResponse { sprite_config: Arc, - sprite_data: HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>, + sprite_data: HashMap<(SpriteKind, usize), Vec>, sprite_col_lights: ColLightInfo, - sprite_mesh: Mesh, } let join_handle = std::thread::spawn(move || { @@ -393,14 +363,17 @@ impl SpriteRenderContext { let sprite_config = Arc::::load_expect("voxygen.voxel.sprite_manifest").cloned(); - let max_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32); + let max_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); let mut greedy = GreedyMesh::new(max_size); - let mut sprite_mesh = Mesh::new(); + let mut locals_buffer = [SpriteLocals::default(); 8]; let sprite_config_ = &sprite_config; // NOTE: Tracks the start vertex of the next model to be meshed. + let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter() .filter_map(|kind| Some((kind, kind.elim_case_pure(&sprite_config_.0).as_ref()?))) .flat_map(|(kind, sprite_config)| { + let wind_sway = sprite_config.wind_sway; sprite_config.variations.iter().enumerate().map( move |( variation, @@ -428,60 +401,68 @@ impl SpriteRenderContext { ) .unwrap_or(zero); let max_model_size = Vec3::new(31.0, 31.0, 63.0); - let model_scale = - max_model_size.map2(model_size, |max_sz: f32, cur_sz| { - let scale = max_sz / max_sz.max(cur_sz as f32); - if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz { - scale - 0.001 - } else { - scale - } - }); - move |greedy: &mut GreedyMesh, sprite_mesh: &mut Mesh| { - let lod_sprite_data = scaled.map(|lod_scale_orig| { - let lod_scale = model_scale - * if lod_scale_orig == 1.0 { - Vec3::broadcast(1.0) - } else { - lod_axes * lod_scale_orig - + lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 }) - }; + let model_scale = max_model_size.map2(model_size, |max_sz: f32, cur_sz| { + let scale = max_sz / max_sz.max(cur_sz as f32); + if scale < 1.0 && (cur_sz as f32 * scale).ceil() > max_sz { + scale - 0.001 + } else { + scale + } + }); + let sprite_mat: Mat4 = + Mat4::translation_3d(offset).scaled_3d(SPRITE_SCALE); + move |greedy: &mut GreedyMesh| { + ( + (kind, variation), + scaled + .iter() + .map(|&lod_scale_orig| { + let lod_scale = model_scale + * if lod_scale_orig == 1.0 { + Vec3::broadcast(1.0) + } else { + lod_axes * lod_scale_orig + + lod_axes + .map(|e| if e == 0.0 { 1.0 } else { 0.0 }) + }; + // Mesh generation exclusively acts using side effects; it + // has no + // interesting return value, but updates the mesh. + let mut opaque_mesh = Mesh::new(); + Meshable::::generate_mesh( + Segment::from(&model.read().0).scaled_by(lod_scale), + (greedy, &mut opaque_mesh, false), + ); - // Get starting page count of opaque mesh - let start_page_num = sprite_mesh.vertices().len() - / SPRITE_VERT_PAGE_SIZE as usize; - // Mesh generation exclusively acts using side effects; it - // has no interesting return value, but updates the mesh. - generate_mesh_base_vol_sprite( - Segment::from(&model.read().0).scaled_by(lod_scale), - (greedy, sprite_mesh, false), - ); - // Get the number of pages after the model was meshed - let end_page_num = (sprite_mesh.vertices().len() - + SPRITE_VERT_PAGE_SIZE as usize - - 1) - / SPRITE_VERT_PAGE_SIZE as usize; - // Fill the current last page up with degenerate verts - sprite_mesh.vertices_mut_vec().resize_with( - end_page_num * SPRITE_VERT_PAGE_SIZE as usize, - SpriteVertex::default, - ); + let sprite_scale = Vec3::one() / lod_scale; + let sprite_mat: Mat4 = + sprite_mat * Mat4::scaling_3d(sprite_scale); + locals_buffer.iter_mut().enumerate().for_each( + |(ori, locals)| { + let sprite_mat = sprite_mat + .rotated_z(f32::consts::PI * 0.25 * ori as f32); + *locals = SpriteLocals::new( + sprite_mat, + sprite_scale, + offset, + wind_sway, + ); + }, + ); - let sprite_scale = Vec3::one() / lod_scale; - - SpriteData { - vert_pages: start_page_num as u32..end_page_num as u32, - scale: sprite_scale, - offset, - } - }); - - ((kind, variation), lod_sprite_data) + SpriteDataResponse { + model: opaque_mesh, + offset, + locals: locals_buffer, + } + }) + .collect::>(), + ) } }, ) }) - .map(|f| f(&mut greedy, &mut sprite_mesh)) + .map(|mut f| f(&mut greedy)) .collect(); let sprite_col_lights = greedy.finalize(); @@ -490,7 +471,6 @@ impl SpriteRenderContext { sprite_config, sprite_data, sprite_col_lights, - sprite_mesh, } }); @@ -505,7 +485,6 @@ impl SpriteRenderContext { sprite_config, sprite_data, sprite_col_lights, - sprite_mesh, } = join_handle .take() .expect( @@ -515,19 +494,41 @@ impl SpriteRenderContext { .join() .unwrap(); - let sprite_col_lights = - pipelines::shadow::create_col_lights(renderer, &sprite_col_lights); - let sprite_col_lights = renderer.sprite_bind_col_light(sprite_col_lights); - - // Write sprite model to a 1D texture - let sprite_verts_buffer = renderer.create_sprite_verts(sprite_mesh); + let sprite_data = sprite_data + .into_iter() + .map(|(key, models)| { + ( + key, + models + .into_iter() + .map( + |SpriteDataResponse { + locals, + model, + offset, + }| { + SpriteData { + locals: renderer + .create_consts(&locals) + .expect("Failed to upload sprite locals to the GPU!"), + model: renderer.create_model(&model).expect( + "Failed to upload sprite model data to the GPU!", + ), + offset, + } + }, + ) + .collect(), + ) + }) + .collect(); + let sprite_col_lights = ShadowPipeline::create_col_lights(renderer, &sprite_col_lights) + .expect("Failed to upload sprite color and light data to the GPU!"); Self { - // TODO: these are all Arcs, would it makes sense to factor out the Arc? sprite_config: Arc::clone(&sprite_config), sprite_data: Arc::new(sprite_data), - sprite_col_lights: Arc::new(sprite_col_lights), - sprite_verts_buffer: Arc::new(sprite_verts_buffer), + sprite_col_lights, } }; Box::new(move |renderer| init.get_or_init(|| closure(renderer)).clone()) @@ -535,12 +536,7 @@ impl SpriteRenderContext { } impl Terrain { - pub fn new( - renderer: &mut Renderer, - global_model: &GlobalModel, - lod_data: &LodData, - sprite_render_context: SpriteRenderContext, - ) -> Self { + pub fn new(renderer: &mut Renderer, sprite_render_context: SpriteRenderContext) -> Self { // Create a new mpsc (Multiple Produced, Single Consumer) pair for communicating // with worker threads that are meshing chunks. let (send, recv) = channel::unbounded(); @@ -560,22 +556,26 @@ impl Terrain { mesh_recv_overflow: 0.0, sprite_data: sprite_render_context.sprite_data, sprite_col_lights: sprite_render_context.sprite_col_lights, - sprite_globals: renderer.bind_sprite_globals( - global_model, - lod_data, - &sprite_render_context.sprite_verts_buffer, - ), - col_lights: Arc::new(col_lights), + waves: renderer + .create_texture( + &assets::Image::load_expect("voxygen.texture.waves").read().0, + Some(gfx::texture::FilterMethod::Trilinear), + Some(gfx::texture::WrapMode::Tile), + None, + ) + .expect("Failed to create wave texture"), + col_lights, phantom: PhantomData, } } fn make_atlas( renderer: &mut Renderer, - ) -> Result<(AtlasAllocator, ColLights), RenderError> { + ) -> Result<(AtlasAllocator, Texture), RenderError> { span!(_guard, "make_atlas", "Terrain::make_atlas"); let max_texture_size = renderer.max_texture_size(); - let atlas_size = guillotiere::Size::new(max_texture_size as i32, max_texture_size as i32); + let atlas_size = + guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size)); let atlas = AtlasAllocator::with_options(atlas_size, &guillotiere::AllocatorOptions { // TODO: Verify some good empirical constants. small_size_threshold: 128, @@ -583,48 +583,28 @@ impl Terrain { ..guillotiere::AllocatorOptions::default() }); let texture = renderer.create_texture_raw( - &wgpu::TextureDescriptor { - label: Some("Atlas texture"), - size: wgpu::Extent3d { - width: max_texture_size, - height: max_texture_size, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED, - }, - &wgpu::TextureViewDescriptor { - label: Some("Atlas texture view"), - format: Some(wgpu::TextureFormat::Rgba8Unorm), - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, - }, - &wgpu::SamplerDescriptor { - label: Some("Atlas sampler"), - 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::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - }, - ); - let col_light = renderer.terrain_bind_col_light(texture); - Ok((atlas, col_light)) + gfx::texture::Kind::D2( + max_texture_size, + max_texture_size, + gfx::texture::AaMode::Single, + ), + 1_u8, + gfx::memory::Bind::SHADER_RESOURCE, + gfx::memory::Usage::Dynamic, + (0, 0), + gfx::format::Swizzle::new(), + gfx::texture::SamplerInfo::new( + gfx::texture::FilterMethod::Bilinear, + gfx::texture::WrapMode::Clamp, + ), + )?; + Ok((atlas, texture)) } fn remove_chunk_meta(&mut self, _pos: Vec2, chunk: &TerrainChunkData) { // No need to free the allocation if the chunk is not allocated in the current // atlas, since we don't bother tracking it at that point. - if let Some(col_lights) = chunk.col_lights_alloc { + if let Some(col_lights) = chunk.col_lights { self.atlas.deallocate(col_lights); } /* let (zmin, zmax) = chunk.z_bounds; @@ -756,14 +736,9 @@ impl Terrain { scene_data: &SceneData, focus_pos: Vec3, loaded_distance: f32, - camera: &Camera, + view_mat: Mat4, + proj_mat: Mat4, ) -> (Aabb, Vec>, math::Aabr) { - let camera::Dependents { - view_mat, - proj_mat_treeculler, - .. - } = camera.dependents(); - // Remove any models for chunks that have been recently removed. // Note: Does this before adding to todo list just in case removed chunks were // replaced with new chunks (although this would probably be recorded as @@ -972,6 +947,7 @@ impl Terrain { break; } + // Find the area of the terrain we want. Because meshing needs to compute things // like ambient occlusion and edge elision, we also need the borders // of the chunk's neighbours too (hence the `- 1` and `+ 1`). let aabr = Aabr { @@ -1038,7 +1014,7 @@ impl Terrain { skip_remesh, started_tick, volume, - max_texture_size as u16, + max_texture_size, chunk, aabb, &sprite_data, @@ -1068,12 +1044,18 @@ impl Terrain { // data structure (convert the mesh to a model first of course). Some(todo) if response.started_tick <= todo.started_tick => { let started_tick = todo.started_tick; - - let sprite_instances = response.sprite_instances.map(|instances| { - renderer - .create_instances(&instances) - .expect("Failed to upload chunk sprite instances to the GPU!") - }); + let sprite_instances = response + .sprite_instances + .into_iter() + .map(|(kind, instances)| { + ( + kind, + renderer + .create_instances(&instances) + .expect("Failed to upload chunk sprite instances to the GPU!"), + ) + }) + .collect(); if let Some(mesh) = response.mesh { // Full update, insert the whole chunk. @@ -1088,63 +1070,89 @@ impl Terrain { let atlas = &mut self.atlas; let chunks = &mut self.chunks; let col_lights = &mut self.col_lights; - let alloc_size = - guillotiere::Size::new(i32::from(tex_size.x), i32::from(tex_size.y)); + let allocation = atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .unwrap_or_else(|| { + // Atlas allocation failure: try allocating a new texture and atlas. + let (new_atlas, new_col_lights) = Self::make_atlas(renderer) + .expect("Failed to create atlas texture"); - let allocation = atlas.allocate(alloc_size).unwrap_or_else(|| { - // Atlas allocation failure: try allocating a new texture and atlas. - let (new_atlas, new_col_lights) = - Self::make_atlas(renderer).expect("Failed to create atlas texture"); + // We reset the atlas and clear allocations from existing chunks, + // even though we haven't yet + // checked whether the new allocation can fit in + // the texture. This is reasonable because we don't have a fallback + // if a single chunk can't fit in an empty atlas of maximum size. + // + // TODO: Consider attempting defragmentation first rather than just + // always moving everything into the new chunk. + chunks.iter_mut().for_each(|(_, chunk)| { + chunk.col_lights = None; + }); + *atlas = new_atlas; + *col_lights = new_col_lights; - // We reset the atlas and clear allocations from existing chunks, - // even though we haven't yet - // checked whether the new allocation can fit in - // the texture. This is reasonable because we don't have a fallback - // if a single chunk can't fit in an empty atlas of maximum size. - // - // TODO: Consider attempting defragmentation first rather than just - // always moving everything into the new chunk. - chunks.iter_mut().for_each(|(_, chunk)| { - chunk.col_lights_alloc = None; + atlas + .allocate(guillotiere::Size::new( + i32::from(tex_size.x), + i32::from(tex_size.y), + )) + .expect("Chunk data does not fit in a texture of maximum size.") }); - *atlas = new_atlas; - *col_lights = Arc::new(new_col_lights); - - atlas - .allocate(alloc_size) - .expect("Chunk data does not fit in a texture of maximum size.") - }); // NOTE: Cast is safe since the origin was a u16. let atlas_offs = Vec2::new( - allocation.rectangle.min.x as u32, - allocation.rectangle.min.y as u32, + allocation.rectangle.min.x as u16, + allocation.rectangle.min.y as u16, ); - renderer.update_texture( - &col_lights.texture, + if let Err(err) = renderer.update_texture( + col_lights, atlas_offs.into_array(), - tex_size.map(u32::from).into_array(), + tex_size.into_array(), &tex, - ); + ) { + warn!("Failed to update texture: {:?}", err); + } self.insert_chunk(response.pos, TerrainChunkData { load_time, - opaque_model: renderer.create_model(&mesh.opaque_mesh), - fluid_model: renderer.create_model(&mesh.fluid_mesh), - col_lights_alloc: Some(allocation.id), - col_lights: Arc::clone(&self.col_lights), + opaque_model: renderer + .create_model(&mesh.opaque_mesh) + .expect("Failed to upload chunk mesh to the GPU!"), + fluid_model: if mesh.fluid_mesh.vertices().len() > 0 { + Some( + renderer + .create_model(&mesh.fluid_mesh) + .expect("Failed to upload chunk mesh to the GPU!"), + ) + } else { + None + }, + col_lights: Some(allocation.id), + texture: self.col_lights.clone(), light_map: mesh.light_map, glow_map: mesh.glow_map, sprite_instances, - locals: renderer.create_terrain_bound_locals(&[TerrainLocals::new( - Vec3::from( - response.pos.map2(VolGrid2d::::chunk_size(), |e, sz| { - e as f32 * sz as f32 - }), - ), - atlas_offs, - load_time, - )]), + locals: renderer + .create_consts(&[TerrainLocals { + model_offs: Vec3::from( + response.pos.map2(VolGrid2d::::chunk_size(), |e, sz| { + e as f32 * sz as f32 + }), + ) + .into_array(), + atlas_offs: Vec4::new( + i32::from(atlas_offs.x), + i32::from(atlas_offs.y), + 0, + 0, + ) + .into_array(), + load_time, + }]) + .expect("Failed to upload chunk locals to the GPU!"), visible: Visibility { in_range: false, in_frustum: false, @@ -1178,7 +1186,7 @@ impl Terrain { span!(guard, "Construct view frustum"); let focus_off = focus_pos.map(|e| e.trunc()); let frustum = Frustum::from_modelview_projection( - (proj_mat_treeculler * view_mat * Mat4::translation_3d(-focus_off)).into_col_arrays(), + (proj_mat * view_mat * Mat4::translation_3d(-focus_off)).into_col_arrays(), ); drop(guard); @@ -1255,13 +1263,10 @@ impl Terrain { let focus_off = math::Vec3::from(focus_off); let visible_bounds_fine = visible_bounding_box.as_::(); let inv_proj_view = - math::Mat4::from_col_arrays((proj_mat_treeculler * view_mat).into_col_arrays()) + math::Mat4::from_col_arrays((proj_mat * view_mat).into_col_arrays()) .as_::() .inverted(); let ray_direction = math::Vec3::::from(ray_direction); - // NOTE: We use proj_mat_treeculler here because - // calc_focused_light_volume_points makes the assumption that the - // near plane lies before the far plane. let visible_light_volume = math::calc_focused_light_volume_points( inv_proj_view, ray_direction.as_::(), @@ -1363,12 +1368,18 @@ impl Terrain { pub fn shadow_chunk_count(&self) -> usize { self.shadow_chunks.len() } - pub fn render_shadows<'a>( - &'a self, - drawer: &mut TerrainShadowDrawer<'_, 'a>, + pub fn render_shadows( + &self, + renderer: &mut Renderer, + global: &GlobalModel, + (is_daylight, light_data): super::LightData, focus_pos: Vec3, ) { span!(_guard, "render_shadows", "Terrain::render_shadows"); + if !renderer.render_mode().shadow.is_map() { + return; + }; + let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); @@ -1385,80 +1396,77 @@ impl Terrain { // NOTE: We also render shadows for dead chunks that were found to still be // potential shadow casters, to avoid shadows suddenly disappearing at // very steep sun angles (e.g. sunrise / sunset). - chunk_iter - .filter(|chunk| chunk.can_shadow_sun()) - .chain(self.shadow_chunks.iter().map(|(_, chunk)| chunk)) - .filter_map(|chunk| { - chunk - .opaque_model - .as_ref() - .map(|model| (model, &chunk.locals)) - }) - .for_each(|(model, locals)| drawer.draw(model, locals)); - } - - pub fn chunks_for_point_shadows( - &self, - focus_pos: Vec3, - ) -> impl Clone - + Iterator< - Item = ( - &Model, - &pipelines::terrain::BoundLocals, - ), - > { - let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { - (e as i32).div_euclid(sz as i32) - }); - - let chunk_iter = Spiral2d::new() - .filter_map(move |rpos| { - let pos = focus_chunk + rpos; - self.chunks.get(&pos) - }) - .take(self.chunks.len()); + if is_daylight { + chunk_iter + .clone() + .filter(|chunk| chunk.can_shadow_sun()) + .chain(self.shadow_chunks.iter().map(|(_, chunk)| chunk)) + .for_each(|chunk| { + // Directed light shadows. + renderer.render_terrain_shadow_directed( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + }); + } // Point shadows // // NOTE: We don't bother retaining chunks unless they cast sun shadows, so we // don't use `shadow_chunks` here. - chunk_iter - .filter(|chunk| chunk.can_shadow_point) - .filter_map(|chunk| { - chunk - .opaque_model - .as_ref() - .map(|model| (model, &chunk.locals)) - }) + light_data.iter().take(1).for_each(|_light| { + chunk_iter.clone().for_each(|chunk| { + if chunk.can_shadow_point { + renderer.render_shadow_point( + &chunk.opaque_model, + global, + &chunk.locals, + &global.shadow_mats, + ); + } + }); + }); } - pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>, focus_pos: Vec3) { + pub fn render( + &self, + renderer: &mut Renderer, + global: &GlobalModel, + lod: &LodData, + focus_pos: Vec3, + ) { span!(_guard, "render", "Terrain::render"); - let mut drawer = drawer.draw_terrain(); - let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| { (e as i32).div_euclid(sz as i32) }); - Spiral2d::new() + let chunk_iter = Spiral2d::new() .filter_map(|rpos| { let pos = focus_chunk + rpos; - self.chunks.get(&pos) + self.chunks.get(&pos).map(|c| (pos, c)) }) - .take(self.chunks.len()) - .filter(|chunk| chunk.visible.is_visible()) - .filter_map(|chunk| { - chunk - .opaque_model - .as_ref() - .map(|model| (model, &chunk.col_lights, &chunk.locals)) - }) - .for_each(|(model, col_lights, locals)| drawer.draw(model, col_lights, locals)); + .take(self.chunks.len()); + + for (_, chunk) in chunk_iter { + if chunk.visible.is_visible() { + renderer.render_terrain_chunk( + &chunk.opaque_model, + &chunk.texture, + global, + &chunk.locals, + lod, + ); + } + } } - pub fn render_translucent<'a>( - &'a self, - drawer: &mut FirstPassDrawer<'a>, + pub fn render_translucent( + &self, + renderer: &mut Renderer, + global: &GlobalModel, + lod: &LodData, focus_pos: Vec3, cam_pos: Vec3, sprite_render_distance: f32, @@ -1480,54 +1488,68 @@ impl Terrain { // TODO: move to separate functions span!(guard, "Terrain sprites"); let chunk_size = V::RECT_SIZE.map(|e| e as f32); - - let sprite_low_detail_distance = sprite_render_distance * 0.75; - let sprite_mid_detail_distance = sprite_render_distance * 0.5; - let sprite_hid_detail_distance = sprite_render_distance * 0.35; - let sprite_high_detail_distance = sprite_render_distance * 0.15; - - let mut sprite_drawer = drawer.draw_sprites(&self.sprite_globals, &self.sprite_col_lights); - chunk_iter - .clone() - .filter(|(_, c)| c.visible.is_visible()) - .for_each(|(pos, chunk)| { - // Skip chunk if it has no sprites - if chunk.sprite_instances[0].count() == 0 { - return; - } + let chunk_mag = (chunk_size * (f32::consts::SQRT_2 * 0.5)).magnitude_squared(); + for (pos, chunk) in chunk_iter.clone() { + if chunk.visible.is_visible() { + let sprite_low_detail_distance = sprite_render_distance * 0.75; + let sprite_mid_detail_distance = sprite_render_distance * 0.5; + let sprite_hid_detail_distance = sprite_render_distance * 0.35; + let sprite_high_detail_distance = sprite_render_distance * 0.15; let chunk_center = pos.map2(chunk_size, |e, sz| (e as f32 + 0.5) * sz); let focus_dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); - let dist_sqrd = Aabr { - min: chunk_center - chunk_size * 0.5, - max: chunk_center + chunk_size * 0.5, - } - .projected_point(cam_pos.xy()) - .distance_squared(cam_pos.xy()); - + let dist_sqrd = + Vec2::from(cam_pos) + .distance_squared(chunk_center) + .min(Vec2::from(cam_pos).distance_squared(chunk_center - chunk_size * 0.5)) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center - chunk_size.x * 0.5 + chunk_size.y * 0.5, + )) + .min( + Vec2::from(cam_pos).distance_squared(chunk_center + chunk_size.x * 0.5), + ) + .min(Vec2::from(cam_pos).distance_squared( + chunk_center + chunk_size.x * 0.5 - chunk_size.y * 0.5, + )); if focus_dist_sqrd < sprite_render_distance.powi(2) { - let lod_level = if dist_sqrd < sprite_high_detail_distance.powi(2) { - 0 - } else if dist_sqrd < sprite_hid_detail_distance.powi(2) { - 1 - } else if dist_sqrd < sprite_mid_detail_distance.powi(2) { - 2 - } else if dist_sqrd < sprite_low_detail_distance.powi(2) { - 3 - } else { - 4 - }; - - sprite_drawer.draw(&chunk.locals, &chunk.sprite_instances[lod_level]); + for (kind, instances) in (&chunk.sprite_instances).into_iter() { + let SpriteData { model, locals, .. } = if kind + .0 + .elim_case_pure(&self.sprite_config.0) + .as_ref() + .map(|config| config.wind_sway >= 0.4) + .unwrap_or(false) + && dist_sqrd <= chunk_mag + || dist_sqrd < sprite_high_detail_distance.powi(2) + { + &self.sprite_data[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powi(2) { + &self.sprite_data[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powi(2) { + &self.sprite_data[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powi(2) { + &self.sprite_data[&kind][3] + } else { + &self.sprite_data[&kind][4] + }; + renderer.render_sprites( + model, + &self.sprite_col_lights, + global, + &chunk.locals, + locals, + &instances, + lod, + ); + } } - }); - drop(sprite_drawer); + } + } drop(guard); // Translucent - span!(guard, "Fluid chunks"); - let mut fluid_drawer = drawer.draw_fluid(); chunk_iter + .clone() .filter(|(_, chunk)| chunk.visible.is_visible()) .filter_map(|(_, chunk)| { chunk @@ -1539,12 +1561,13 @@ impl Terrain { .into_iter() .rev() // Render back-to-front .for_each(|(model, locals)| { - fluid_drawer.draw( + renderer.render_fluid_chunk( model, + global, locals, + lod, + &self.waves, ) }); - drop(fluid_drawer); - drop(guard); } } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 32c584af77..5e96e12bf1 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -39,12 +39,11 @@ use crate::{ key_state::KeyState, menu::char_selection::CharSelectionState, render::Renderer, - scene::{camera, terrain::Interaction, CameraMode, DebugShapeId, Scene, SceneData}, + scene::{camera, terrain::Interaction, CameraMode, Scene, SceneData}, settings::Settings, window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; -use hashbrown::HashMap; use settings_change::Language::ChangeLanguage; /// The action to perform after a tick @@ -74,7 +73,6 @@ pub struct SessionState { selected_entity: Option<(specs::Entity, std::time::Instant)>, interactable: Option, saved_zoom_dist: Option, - hitboxes: HashMap, } /// Represents an active game session (i.e., the one being played). @@ -122,7 +120,6 @@ impl SessionState { selected_entity: None, interactable: None, saved_zoom_dist: None, - hitboxes: HashMap::new(), } } @@ -142,8 +139,6 @@ impl SessionState { span!(_guard, "tick", "Session::tick"); let mut client = self.client.borrow_mut(); - self.scene - .maintain_debug_hitboxes(&client, &global_state.settings, &mut self.hitboxes); for event in client.tick(self.inputs.clone(), dt, crate::ecs::sys::add_local_systems)? { match event { client::Event::Chat(m) => { @@ -1394,16 +1389,7 @@ impl PlayState for SessionState { /// This method should be called once per frame. fn render(&mut self, renderer: &mut Renderer, settings: &Settings) { span!(_guard, "render", "::render"); - let mut drawer = match renderer - .start_recording_frame(self.scene.global_bind_group()) - .expect("Unrecoverable render error when starting a new frame!") - { - Some(d) => d, - // Couldn't get swap chain texture this frame - None => return, - }; - - // Render world + // Render the screen using the global renderer { let client = self.client.borrow(); @@ -1425,27 +1411,16 @@ impl PlayState for SessionState { particles_enabled: settings.graphics.particles_enabled, is_aiming: self.is_aiming, }; - self.scene.render( - &mut drawer, + renderer, client.state(), client.entity(), client.get_tick(), &scene_data, ); } - - // Clouds - if let Some(mut second_pass) = drawer.second_pass() { - second_pass.draw_clouds(); - } - // PostProcess and UI - let mut third_pass = drawer.third_pass(); - third_pass.draw_postprocess(); // Draw the UI to the screen - if let Some(mut ui_drawer) = third_pass.draw_ui() { - self.hud.render(&mut ui_drawer); - }; // Note: this semicolon is needed for the third_pass borrow to be dropped before it's lifetime ends + self.hud.render(renderer, self.scene.globals()); } } diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index cbb4fdaa64..a6d2f4c7c8 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -96,7 +96,6 @@ pub enum Interface { SpeechBubbleIcon(bool), ToggleHelp(bool), ToggleDebug(bool), - ToggleHitboxes(bool), ToggleTips(bool), CrosshairTransp(f32), @@ -447,9 +446,6 @@ impl SettingsChange { Interface::ToggleDebug(toggle_debug) => { settings.interface.toggle_debug = toggle_debug; }, - Interface::ToggleHitboxes(toggle_hitboxes) => { - settings.interface.toggle_hitboxes = toggle_hitboxes; - }, Interface::ToggleTips(loading_tips) => { settings.interface.loading_tips = loading_tips; }, diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index 0e632d4765..b3101b7245 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -10,7 +10,6 @@ use vek::*; #[serde(default)] pub struct InterfaceSettings { pub toggle_debug: bool, - pub toggle_hitboxes: bool, pub sct: bool, pub sct_player_batch: bool, pub sct_damage_batch: bool, @@ -45,7 +44,6 @@ impl Default for InterfaceSettings { fn default() -> Self { Self { toggle_debug: false, - toggle_hitboxes: false, sct: true, sct_player_batch: false, sct_damage_batch: false, diff --git a/voxygen/src/ui/cache.rs b/voxygen/src/ui/cache.rs index dcf5041b8e..fe2eaa13f5 100644 --- a/voxygen/src/ui/cache.rs +++ b/voxygen/src/ui/cache.rs @@ -1,6 +1,6 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ - render::{Mesh, Renderer, Texture, UiTextureBindGroup, UiVertex}, + render::{Mesh, Renderer, Texture, UiPipeline}, Error, }; use conrod_core::{text::GlyphCache, widget::Id}; @@ -8,50 +8,44 @@ use hashbrown::HashMap; use vek::*; // Multiplied by current window size -const GLYPH_CACHE_SIZE: u32 = 1; +const GLYPH_CACHE_SIZE: u16 = 1; // Glyph cache tolerances const SCALE_TOLERANCE: f32 = 0.5; const POSITION_TOLERANCE: f32 = 0.5; -type TextCache = HashMap>; +type TextCache = HashMap>; pub struct Cache { // Map from text ids to their positioned glyphs. text_cache: TextCache, glyph_cache: GlyphCache<'static>, - glyph_cache_tex: (Texture, UiTextureBindGroup), + glyph_cache_tex: Texture, graphic_cache: GraphicCache, } // TODO: Should functions be returning UiError instead of Error? impl Cache { pub fn new(renderer: &mut Renderer) -> Result { - let (w, h) = renderer.resolution().into_tuple(); + let (w, h) = renderer.get_resolution().into_tuple(); let max_texture_size = renderer.max_texture_size(); let glyph_cache_dims = Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); - let glyph_cache_tex = { - let tex = renderer.create_dynamic_texture(glyph_cache_dims); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; - Ok(Self { text_cache: Default::default(), glyph_cache: GlyphCache::builder() - .dimensions(glyph_cache_dims.x, glyph_cache_dims.y) + .dimensions(glyph_cache_dims.x as u32, glyph_cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) .position_tolerance(POSITION_TOLERANCE) .build(), - glyph_cache_tex, + glyph_cache_tex: renderer.create_dynamic_texture(glyph_cache_dims.map(|e| e as u16))?, graphic_cache: GraphicCache::new(renderer), }) } - pub fn glyph_cache_tex(&self) -> &(Texture, UiTextureBindGroup) { &self.glyph_cache_tex } + pub fn glyph_cache_tex(&self) -> &Texture { &self.glyph_cache_tex } pub fn cache_mut_and_tex( &mut self, @@ -59,7 +53,7 @@ impl Cache { &mut GraphicCache, &mut TextCache, &mut GlyphCache<'static>, - &(Texture, UiTextureBindGroup), + &Texture, ) { ( &mut self.graphic_cache, @@ -88,18 +82,14 @@ impl Cache { self.text_cache.clear(); let max_texture_size = renderer.max_texture_size(); let cache_dims = renderer - .resolution() + .get_resolution() .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); self.glyph_cache = GlyphCache::builder() - .dimensions(cache_dims.x, cache_dims.y) + .dimensions(cache_dims.x as u32, cache_dims.y as u32) .scale_tolerance(SCALE_TOLERANCE) .position_tolerance(POSITION_TOLERANCE) .build(); - self.glyph_cache_tex = { - let tex = renderer.create_dynamic_texture(cache_dims); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; + self.glyph_cache_tex = renderer.create_dynamic_texture(cache_dims.map(|e| e as u16))?; Ok(()) } } diff --git a/voxygen/src/ui/graphic/mod.rs b/voxygen/src/ui/graphic/mod.rs index ac79c42d5c..241e14dbbc 100644 --- a/voxygen/src/ui/graphic/mod.rs +++ b/voxygen/src/ui/graphic/mod.rs @@ -4,7 +4,7 @@ mod renderer; pub use renderer::{SampleStrat, Transform}; use crate::{ - render::{Renderer, Texture, UiTextureBindGroup}, + render::{RenderError, Renderer, Texture}, ui::KeyedJobs, }; use common::{figure::Segment, slowjob::SlowJobPool}; @@ -51,7 +51,7 @@ pub enum Rotation { /// Fraction of the total graphic cache size const ATLAS_CUTOFF_FRAC: f32 = 0.2; /// Multiplied by current window size -const GRAPHIC_CACHE_RELATIVE_SIZE: u32 = 1; +const GRAPHIC_CACHE_RELATIVE_SIZE: u16 = 1; #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub struct Id(u32); @@ -142,7 +142,7 @@ pub struct GraphicCache { // Atlases with the index of their texture in the textures vec atlases: Vec<(SimpleAtlasAllocator, usize)>, - textures: Vec<(Texture, UiTextureBindGroup)>, + textures: Vec, // Stores the location of graphics rendered at a particular resolution and cached on the cpu cache_map: HashMap, @@ -191,7 +191,7 @@ impl GraphicCache { pub fn get_graphic(&self, id: Id) -> Option<&Graphic> { self.graphic_map.get(&id) } /// Used to acquire textures for rendering - pub fn get_tex(&self, id: TexId) -> &(Texture, UiTextureBindGroup) { + pub fn get_tex(&self, id: TexId) -> &Texture { self.textures.get(id.0).expect("Invalid TexId used") } @@ -293,7 +293,7 @@ impl GraphicCache { // color. assert!(border.is_none()); // Transfer to the gpu - upload_image(renderer, aabr, &textures[idx].0, &image); + upload_image(renderer, aabr, &textures[idx], &image); } return Some((transformed_aabr(aabr.map(|e| e as f64)), TexId(idx))); @@ -314,7 +314,7 @@ impl GraphicCache { // Graphics over a particular size are sent to their own textures let location = if let Some(border_color) = border_color { // Create a new immutable texture. - let texture = create_image(renderer, image, border_color); + let texture = create_image(renderer, image, border_color).unwrap(); // NOTE: All mutations happen only after the upload succeeds! let index = textures.len(); textures.push(texture); @@ -335,7 +335,7 @@ impl GraphicCache { valid: true, aabr, }); - upload_image(renderer, aabr, &textures[texture_idx].0, &image); + upload_image(renderer, aabr, &textures[texture_idx], &image); break; } } @@ -355,7 +355,7 @@ impl GraphicCache { let atlas_idx = atlases.len(); textures.push(texture); atlases.push((atlas, tex_idx)); - upload_image(renderer, aabr, &textures[tex_idx].0, &image); + upload_image(renderer, aabr, &textures[tex_idx], &image); CachedDetails::Atlas { atlas_idx, valid: true, @@ -365,11 +365,7 @@ impl GraphicCache { } } else { // Create a texture just for this - let texture = { - let tex = renderer.create_dynamic_texture(dims.map(|e| e as u32)); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; + let texture = renderer.create_dynamic_texture(dims).unwrap(); // NOTE: All mutations happen only after the texture creation succeeds! let index = textures.len(); textures.push(texture); @@ -380,7 +376,7 @@ impl GraphicCache { // Note texture should always match the cached dimensions max: dims, }, - &textures[index].0, + &textures[index], &image, ); CachedDetails::Texture { index, valid: true } @@ -444,28 +440,20 @@ fn draw_graphic( } } -fn atlas_size(renderer: &Renderer) -> Vec2 { +fn atlas_size(renderer: &Renderer) -> Vec2 { let max_texture_size = renderer.max_texture_size(); - renderer.resolution().map(|e| { + renderer.get_resolution().map(|e| { (e * GRAPHIC_CACHE_RELATIVE_SIZE) .max(512) .min(max_texture_size) }) } -fn create_atlas_texture( - renderer: &mut Renderer, -) -> (SimpleAtlasAllocator, (Texture, UiTextureBindGroup)) { +fn create_atlas_texture(renderer: &mut Renderer) -> (SimpleAtlasAllocator, Texture) { let size = atlas_size(renderer); - // Note: here we assume the atlas size is under i32::MAX - let atlas = SimpleAtlasAllocator::new(size2(size.x as i32, size.y as i32)); - let texture = { - let tex = renderer.create_dynamic_texture(size); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; - + let atlas = SimpleAtlasAllocator::new(size2(i32::from(size.x), i32::from(size.y))); + let texture = renderer.create_dynamic_texture(size).unwrap(); (atlas, texture) } @@ -478,34 +466,29 @@ fn aabr_from_alloc_rect(rect: guillotiere::Rectangle) -> Aabr { } fn upload_image(renderer: &mut Renderer, aabr: Aabr, tex: &Texture, image: &RgbaImage) { - let aabr = aabr.map(|e| e as u32); let offset = aabr.min.into_array(); let size = aabr.size().into_array(); - renderer.update_texture( + if let Err(e) = renderer.update_texture( tex, offset, size, // NOTE: Rgba texture, so each pixel is 4 bytes, ergo this cannot fail. // We make the cast parameters explicit for clarity. - bytemuck::cast_slice::(&image), - ); + gfx::memory::cast_slice::(&image), + ) { + warn!(?e, "Failed to update texture"); + } } fn create_image( renderer: &mut Renderer, image: RgbaImage, - _border_color: Rgba, // See TODO below -) -> (Texture, UiTextureBindGroup) { - let tex = renderer - .create_texture( - &DynamicImage::ImageRgba8(image), - None, - //TODO: either use the desktop only border color or just emulate this - // Some(border_color.into_array().into()), - Some(wgpu::AddressMode::ClampToBorder), - ) - .expect("create_texture only panics is non ImageRbga8 is passed"); - let bind = renderer.ui_bind_texture(&tex); - - (tex, bind) + border_color: Rgba, +) -> Result { + renderer.create_texture( + &DynamicImage::ImageRgba8(image), + None, + Some(gfx::texture::WrapMode::Border), + Some(border_color.into_array().into()), + ) } diff --git a/voxygen/src/ui/ice/cache.rs b/voxygen/src/ui/ice/cache.rs index aeb629bc88..2757450571 100644 --- a/voxygen/src/ui/ice/cache.rs +++ b/voxygen/src/ui/ice/cache.rs @@ -1,6 +1,6 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ - render::{Renderer, Texture, UiTextureBindGroup}, + render::{Renderer, Texture}, Error, }; use common::assets::{self, AssetExt}; @@ -12,7 +12,7 @@ use std::{ use vek::*; // Multiplied by current window size -const GLYPH_CACHE_SIZE: u32 = 1; +const GLYPH_CACHE_SIZE: u16 = 1; // Glyph cache tolerances // TODO: consider scaling based on dpi as well as providing as an option to the // user @@ -46,42 +46,36 @@ pub struct FontId(pub(super) glyph_brush::FontId); pub struct Cache { glyph_brush: RefCell, - glyph_cache_tex: (Texture, UiTextureBindGroup), + glyph_cache_tex: Texture, graphic_cache: GraphicCache, } // TODO: Should functions be returning UiError instead of Error? impl Cache { pub fn new(renderer: &mut Renderer, default_font: Font) -> Result { - let (w, h) = renderer.resolution().into_tuple(); + let (w, h) = renderer.get_resolution().into_tuple(); let max_texture_size = renderer.max_texture_size(); let glyph_cache_dims = - Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); + Vec2::new(w, h).map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); let glyph_brush = GlyphBrushBuilder::using_font(default_font) - .initial_cache_size((glyph_cache_dims.x, glyph_cache_dims.y)) + .initial_cache_size((glyph_cache_dims.x as u32, glyph_cache_dims.y as u32)) .draw_cache_scale_tolerance(SCALE_TOLERANCE) .draw_cache_position_tolerance(POSITION_TOLERANCE) .build(); - let glyph_cache_tex = { - let tex = renderer.create_dynamic_texture(glyph_cache_dims); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; - Ok(Self { glyph_brush: RefCell::new(glyph_brush), - glyph_cache_tex, + glyph_cache_tex: renderer.create_dynamic_texture(glyph_cache_dims.map(|e| e as u16))?, graphic_cache: GraphicCache::new(renderer), }) } - pub fn glyph_cache_tex(&self) -> &(Texture, UiTextureBindGroup) { &self.glyph_cache_tex } + pub fn glyph_cache_tex(&self) -> &Texture { &self.glyph_cache_tex } - pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphBrush, &(Texture, UiTextureBindGroup)) { + pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphBrush, &Texture) { (self.glyph_brush.get_mut(), &self.glyph_cache_tex) } @@ -123,7 +117,6 @@ impl Cache { self.graphic_cache.replace_graphic(id, graphic) } - // TODO: combine resize functions // Resizes and clears the GraphicCache pub fn resize_graphic_cache(&mut self, renderer: &mut Renderer) { self.graphic_cache.clear_cache(renderer); @@ -133,20 +126,15 @@ impl Cache { pub fn resize_glyph_cache(&mut self, renderer: &mut Renderer) -> Result<(), Error> { let max_texture_size = renderer.max_texture_size(); let cache_dims = renderer - .resolution() - .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size).max(512)); + .get_resolution() + .map(|e| (e * GLYPH_CACHE_SIZE).min(max_texture_size as u16).max(512)); let glyph_brush = self.glyph_brush.get_mut(); *glyph_brush = glyph_brush .to_builder() - .initial_cache_size((cache_dims.x, cache_dims.y)) + .initial_cache_size((cache_dims.x as u32, cache_dims.y as u32)) .build(); - self.glyph_cache_tex = { - let tex = renderer.create_dynamic_texture(cache_dims); - let bind = renderer.ui_bind_texture(&tex); - (tex, bind) - }; - + self.glyph_cache_tex = renderer.create_dynamic_texture(cache_dims.map(|e| e as u16))?; Ok(()) } } diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index 849ab7a76a..4103e1f562 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -14,11 +14,7 @@ use super::{ graphic::{self, Graphic}, scale::{Scale, ScaleMode}, }; -use crate::{ - render::{Renderer, UiDrawer}, - window::Window, - Error, -}; +use crate::{render::Renderer, window::Window, Error}; use common::slowjob::SlowJobPool; use common_base::span; use iced::{mouse, Cache, Size, UserInterface}; @@ -46,7 +42,7 @@ impl IcedUi { let renderer = window.renderer_mut(); let scaled_resolution = scale.scaled_resolution().map(|e| e as f32); - let physical_resolution = renderer.resolution(); + let physical_resolution = scale.physical_resolution(); // TODO: examine how much mem fonts take up and reduce clones if significant Ok(Self { @@ -163,7 +159,7 @@ impl IcedUi { // Avoid panic in graphic cache when minimizing. // Somewhat inefficient for elements that won't change size after a window // resize - let physical_resolution = renderer.resolution(); + let physical_resolution = self.scale.physical_resolution(); if physical_resolution.map(|e| e > 0).reduce_and() { self.renderer .resize(scaled_resolution, physical_resolution, renderer); @@ -218,5 +214,5 @@ impl IcedUi { (messages, mouse_interaction) } - pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.renderer.render(drawer); } + pub fn render(&self, renderer: &mut Renderer) { self.renderer.render(renderer, None); } } diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 83eb8f1320..58435a2ced 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -15,8 +15,8 @@ use super::{ }; use crate::{ render::{ - create_ui_quad, create_ui_quad_vert_gradient, DynamicModel, Mesh, Renderer, UiBoundLocals, - UiDrawer, UiLocals, UiMode, UiVertex, + create_ui_quad, create_ui_quad_vert_gradient, Consts, DynamicModel, Globals, Mesh, + Renderer, UiLocals, UiMode, UiPipeline, }, Error, }; @@ -83,11 +83,12 @@ pub struct IcedRenderer { //image_map: Map<(Image, Rotation)>, cache: Cache, // Model for drawing the ui - model: DynamicModel, + model: DynamicModel, // Consts to specify positions of ingame elements (e.g. Nametags) - ingame_locals: Vec, + ingame_locals: Vec>, // Consts for default ui drawing position (ie the interface) - interface_locals: UiBoundLocals, + interface_locals: Consts, + default_globals: Consts, // Used to delay cache resizing until after current frame is drawn //need_cache_resize: bool, @@ -104,7 +105,7 @@ pub struct IcedRenderer { // Per-frame/update current_state: State, - mesh: Mesh, + mesh: Mesh, glyphs: Vec<(usize, usize, Rgba, Vec2)>, // Output from glyph_brush in the previous frame // It can sometimes ask you to redraw with these instead (idk if that is done with @@ -118,19 +119,18 @@ impl IcedRenderer { pub fn new( renderer: &mut Renderer, scaled_resolution: Vec2, - physical_resolution: Vec2, + physical_resolution: Vec2, default_font: Font, ) -> Result { let (half_res, align, p_scale) = Self::calculate_resolution_dependents(physical_resolution, scaled_resolution); - let interface_locals = renderer.create_ui_bound_locals(&[UiLocals::default()]); - Ok(Self { cache: Cache::new(renderer, default_font)?, draw_commands: Vec::new(), - model: renderer.create_dynamic_model(100), - interface_locals, + model: renderer.create_dynamic_model(100)?, + interface_locals: renderer.create_consts(&[UiLocals::default()])?, + default_globals: renderer.create_consts(&[Globals::default()])?, ingame_locals: Vec::new(), mesh: Mesh::new(), glyphs: Vec::new(), @@ -170,7 +170,7 @@ impl IcedRenderer { pub fn resize( &mut self, scaled_resolution: Vec2, - physical_resolution: Vec2, + physical_resolution: Vec2, renderer: &mut Renderer, ) { self.win_dims = scaled_resolution; @@ -227,20 +227,22 @@ impl IcedRenderer { .push(DrawCommand::plain(self.start..self.mesh.vertices().len()));*/ // Fill in placeholder glyph quads - let (glyph_cache, (cache_tex, _)) = self.cache.glyph_cache_mut_and_tex(); + let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex(); let half_res = self.half_res; let brush_result = glyph_cache.process_queued( |rect, tex_data| { - let offset = rect.min; - let size = [rect.width(), rect.height()]; + let offset = [rect.min[0] as u16, rect.min[1] as u16]; + let size = [rect.width() as u16, rect.height() as u16]; let new_data = tex_data .iter() .map(|x| [255, 255, 255, *x]) .collect::>(); - renderer.update_texture(cache_tex, offset, size, &new_data); + if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) { + tracing::warn!("Failed to update glyph cache texture: {:?}", err); + } }, // Urgh more allocation we don't need |vertex_data| { @@ -311,16 +313,18 @@ impl IcedRenderer { // Create a larger dynamic model if the mesh is larger than the current model // size. - if self.model.len() < self.mesh.vertices().len() { - self.model = renderer.create_dynamic_model(self.mesh.vertices().len() * 4 / 3); + if self.model.vbuf.len() < self.mesh.vertices().len() { + self.model = renderer + .create_dynamic_model(self.mesh.vertices().len() * 4 / 3) + .unwrap(); } // Update model with new mesh. - renderer.update_model(&self.model, &self.mesh, 0); + renderer.update_model(&self.model, &self.mesh, 0).unwrap(); } // Returns (half_res, align) fn calculate_resolution_dependents( - res: Vec2, + res: Vec2, win_dims: Vec2, ) -> (Vec2, Vec2, f32) { let half_res = res.map(|e| e as f32 / 2.0); @@ -331,7 +335,7 @@ impl IcedRenderer { (half_res, align, p_scale) } - fn update_resolution_dependents(&mut self, res: Vec2) { + fn update_resolution_dependents(&mut self, res: Vec2) { let (half_res, align, p_scale) = Self::calculate_resolution_dependents(res, self.win_dims); self.half_res = half_res; self.align = align; @@ -549,9 +553,7 @@ impl IcedRenderer { Some((aabr, tex_id)) => { let cache_dims = graphic_cache .get_tex(tex_id) - .0 .get_dimensions() - .xy() .map(|e| e as f32); let min = Vec2::new(aabr.min.x as f32, aabr.max.y as f32) / cache_dims; let max = Vec2::new(aabr.max.x as f32, aabr.min.y as f32) / cache_dims; @@ -778,26 +780,26 @@ impl IcedRenderer { } } - pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { + pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts>) { span!(_guard, "render", "IcedRenderer::render"); - let mut drawer = drawer.prepare(&self.interface_locals, &self.model, self.window_scissor); + let mut scissor = self.window_scissor; + let globals = maybe_globals.unwrap_or(&self.default_globals); + let mut locals = &self.interface_locals; for draw_command in self.draw_commands.iter() { match draw_command { DrawCommand::Scissor(new_scissor) => { - drawer.set_scissor(*new_scissor); + scissor = *new_scissor; }, DrawCommand::WorldPos(index) => { - drawer.set_locals( - index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]), - ); + locals = index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]); }, DrawCommand::Draw { kind, verts } => { - // TODO: don't make these: assert!(!verts.is_empty()); let tex = match kind { DrawKind::Image(tex_id) => self.cache.graphic_cache().get_tex(*tex_id), DrawKind::Plain => self.cache.glyph_cache_tex(), }; - drawer.draw(&tex.1, verts.clone()); // Note: trivial clone + let model = self.model.submodel(verts.clone()); + renderer.render_ui_element(model, tex, scissor, globals, locals); }, } } @@ -807,7 +809,7 @@ impl IcedRenderer { // Given the the resolution determines the offset needed to align integer // offsets from the center of the sceen to pixels #[inline(always)] -fn align(res: Vec2) -> Vec2 { +fn align(res: Vec2) -> Vec2 { // TODO: does this logic still apply in iced's coordinate system? // If the resolution is odd then the center of the screen will be within the // middle of a pixel so we need to offset by 0.5 pixels to be on the edge of @@ -815,13 +817,13 @@ fn align(res: Vec2) -> Vec2 { res.map(|e| (e & 1) as f32 * 0.5) } -fn default_scissor(physical_resolution: Vec2) -> Aabr { +fn default_scissor(physical_resolution: Vec2) -> Aabr { let (screen_w, screen_h) = physical_resolution.into_tuple(); Aabr { min: Vec2 { x: 0, y: 0 }, max: Vec2 { - x: screen_w as u16, - y: screen_h as u16, + x: screen_w, + y: screen_h, }, } } diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 603cd80eb8..0377ca1b61 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -27,8 +27,8 @@ pub use widgets::{ use crate::{ render::{ - create_ui_quad, create_ui_tri, DynamicModel, Mesh, RenderError, Renderer, UiBoundLocals, - UiDrawer, UiLocals, UiMode, UiVertex, + create_ui_quad, create_ui_tri, Consts, DynamicModel, Globals, Mesh, RenderError, Renderer, + UiLocals, UiMode, UiPipeline, }, window::Window, Error, @@ -109,13 +109,14 @@ pub struct Ui { draw_commands: Vec, // Mesh buffer for UI vertices; we reuse its allocation in order to limit vector reallocations // during redrawing. - mesh: Mesh, + mesh: Mesh, // Model for drawing the ui - model: DynamicModel, + model: DynamicModel, // Consts for default ui drawing position (ie the interface) - interface_locals: UiBoundLocals, + interface_locals: Consts, + default_globals: Consts, // Consts to specify positions of ingame elements (e.g. Nametags) - ingame_locals: Vec, + ingame_locals: Vec>, // Window size for updating scaling window_resized: Option>, // Scale factor changed @@ -128,8 +129,6 @@ pub struct Ui { tooltip_manager: TooltipManager, // Item tooltips manager item_tooltip_manager: ItemTooltipManager, - // Scissor for the whole window - window_scissor: Aabr, } impl Ui { @@ -139,8 +138,6 @@ impl Ui { let renderer = window.renderer_mut(); - let physical_resolution = renderer.resolution(); - let mut ui = UiBuilder::new(win_dims).build(); // NOTE: Since we redraw the actual frame each time whether or not the UI needs // to be updated, there's no reason to set the redraw count higher than @@ -161,16 +158,15 @@ impl Ui { scale.scale_factor_logical(), ); - let interface_locals = renderer.create_ui_bound_locals(&[UiLocals::default()]); - Ok(Self { ui, image_map: Map::new(), cache: Cache::new(renderer)?, draw_commands: Vec::new(), mesh: Mesh::new(), - model: renderer.create_dynamic_model(100), - interface_locals, + model: renderer.create_dynamic_model(100)?, + interface_locals: renderer.create_consts(&[UiLocals::default()])?, + default_globals: renderer.create_consts(&[Globals::default()])?, ingame_locals: Vec::new(), window_resized: None, scale_factor_changed: None, @@ -178,7 +174,6 @@ impl Ui { scale, tooltip_manager, item_tooltip_manager, - window_scissor: default_scissor(physical_resolution), }) } @@ -341,13 +336,12 @@ impl Ui { self.scale.window_resized(new_dims); let (w, h) = self.scale.scaled_resolution().into_tuple(); self.ui.handle_event(Input::Resize(w, h)); - self.window_scissor = default_scissor(renderer.resolution()); // Avoid panic in graphic cache when minimizing. // Avoid resetting cache if window size didn't change // Somewhat inefficient for elements that won't change size after a window // resize - let res = renderer.resolution(); + let res = renderer.get_resolution(); res.x > 0 && res.y > 0 && !(old_w == w && old_h == h) } else { false @@ -395,7 +389,7 @@ impl Ui { }; let (half_res, x_align, y_align) = { - let res = renderer.resolution(); + let res = renderer.get_resolution(); ( res.map(|e| e as f32 / 2.0), (res.x & 1) as f32 * 0.5, @@ -575,15 +569,17 @@ impl Ui { tracing::debug!("Updating glyphs and clearing text cache."); if let Err(err) = glyph_cache.cache_queued(|rect, data| { - let offset = [rect.min.x as u32, rect.min.y as u32]; - let size = [rect.width() as u32, rect.height() as u32]; + let offset = [rect.min.x as u16, rect.min.y as u16]; + let size = [rect.width() as u16, rect.height() as u16]; let new_data = data .iter() .map(|x| [255, 255, 255, *x]) .collect::>(); - renderer.update_texture(&cache_tex.0, offset, size, &new_data); + if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) { + warn!("Failed to update texture: {:?}", err); + } }) { // FIXME: If we actually hit this error, it's still possible we could salvage // things in various ways (for instance, the current queue might have extra @@ -619,7 +615,7 @@ impl Ui { let mut current_state = State::Plain; let mut start = 0; - let window_scissor = self.window_scissor; + let window_scissor = default_scissor(renderer); let mut current_scissor = window_scissor; let mut ingame_local_index = 0; @@ -676,11 +672,7 @@ impl Ui { if intersection.is_valid() && intersection.size().map(|s| s > 0).reduce_and() { intersection } else { - // TODO: What should we return here - // We used to return a zero sized aabr but it's invalid to - // use a zero sized scissor so for now we just don't change - // the scissor. - current_scissor + Aabr::new_empty(Vec2::zero()) } }; if new_scissor != current_scissor { @@ -832,9 +824,7 @@ impl Ui { Some((aabr, tex_id)) => { let cache_dims = graphic_cache .get_tex(tex_id) - .0 .get_dimensions() - .xy() .map(|e| e as f32); let min = Vec2::new(aabr.min.x as f32, aabr.max.y as f32) / cache_dims; let max = Vec2::new(aabr.max.x as f32, aabr.min.y as f32) / cache_dims; @@ -942,7 +932,7 @@ impl Ui { let pos_on_screen = (view_projection_mat * Vec4::from_point(parameters.pos)) .homogenized(); - let visible = if pos_on_screen.z > 0.0 && pos_on_screen.z < 1.0 { + let visible = if pos_on_screen.z > -1.0 && pos_on_screen.z < 1.0 { let x = pos_on_screen.x; let y = pos_on_screen.y; let (w, h) = parameters.dims.into_tuple(); @@ -968,13 +958,15 @@ impl Ui { // Push new position command let world_pos = Vec4::from_point(parameters.pos); if self.ingame_locals.len() > ingame_local_index { - renderer.update_consts( - &mut self.ingame_locals[ingame_local_index], - &[world_pos.into()], - ) + renderer + .update_consts( + &mut self.ingame_locals[ingame_local_index], + &[world_pos.into()], + ) + .unwrap(); } else { self.ingame_locals - .push(renderer.create_ui_bound_locals(&[world_pos.into()])); + .push(renderer.create_consts(&[world_pos.into()]).unwrap()); } self.draw_commands .push(DrawCommand::WorldPos(Some(ingame_local_index))); @@ -1019,45 +1011,48 @@ impl Ui { // Create a larger dynamic model if the mesh is larger than the current model // size. - if self.model.len() < self.mesh.vertices().len() { - self.model = renderer.create_dynamic_model(self.mesh.vertices().len() * 4 / 3); + if self.model.vbuf.len() < self.mesh.vertices().len() { + self.model = renderer + .create_dynamic_model(self.mesh.vertices().len() * 4 / 3) + .unwrap(); } // Update model with new mesh. - renderer.update_model(&self.model, &self.mesh, 0); + renderer.update_model(&self.model, &self.mesh, 0).unwrap(); } - pub fn render<'pass, 'data: 'pass>(&'data self, drawer: &mut UiDrawer<'_, 'pass>) { + pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts>) { span!(_guard, "render", "Ui::render"); - let mut drawer = drawer.prepare(&self.interface_locals, &self.model, self.window_scissor); + let mut scissor = default_scissor(renderer); + let globals = maybe_globals.unwrap_or(&self.default_globals); + let mut locals = &self.interface_locals; for draw_command in self.draw_commands.iter() { match draw_command { DrawCommand::Scissor(new_scissor) => { - drawer.set_scissor(*new_scissor); + scissor = *new_scissor; }, DrawCommand::WorldPos(index) => { - drawer.set_locals( - index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]), - ); + locals = index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]); }, DrawCommand::Draw { kind, verts } => { let tex = match kind { DrawKind::Image(tex_id) => self.cache.graphic_cache().get_tex(*tex_id), DrawKind::Plain => self.cache.glyph_cache_tex(), }; - drawer.draw(&tex.1, verts.clone()); // Note: trivial clone + let model = self.model.submodel(verts.clone()); + renderer.render_ui_element(model, tex, scissor, globals, locals); }, } } } } -fn default_scissor(physical_resolution: Vec2) -> Aabr { - let (screen_w, screen_h) = physical_resolution.into_tuple(); +fn default_scissor(renderer: &Renderer) -> Aabr { + let (screen_w, screen_h) = renderer.get_resolution().into_tuple(); Aabr { min: Vec2 { x: 0, y: 0 }, max: Vec2 { - x: screen_w as u16, - y: screen_h as u16, + x: screen_w, + y: screen_h, }, } } diff --git a/voxygen/src/ui/scale.rs b/voxygen/src/ui/scale.rs index a609a6c80c..1d6c1e890c 100644 --- a/voxygen/src/ui/scale.rs +++ b/voxygen/src/ui/scale.rs @@ -103,6 +103,11 @@ impl Scale { /// Get logical window size pub fn logical_resolution(&self) -> Vec2 { self.window_dims } + /// Get physical window size + pub fn physical_resolution(&self) -> Vec2 { + (self.window_dims * self.scale_factor).map(|e| e.round() as u16) + } + // Transform point from logical to scaled coordinates. pub fn scale_point(&self, point: Vec2) -> Vec2 { point / self.scale_factor_logical() } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index e72590f769..06179e860a 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1,6 +1,6 @@ use crate::{ controller::*, - render::Renderer, + render::{Renderer, WinColorFmt, WinDepthFmt}, settings::{ControlSettings, Settings}, ui, Error, }; @@ -10,8 +10,9 @@ use gilrs::{EventType, Gilrs}; use hashbrown::HashMap; use itertools::Itertools; use keyboard_keynames::key_layout::KeyLayout; +use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt}; use serde::{Deserialize, Serialize}; -use tracing::{error, warn}; +use tracing::{error, info, warn}; use vek::*; use winit::monitor::VideoMode; @@ -518,7 +519,7 @@ impl KeyMouse { pub struct Window { renderer: Renderer, - window: winit::window::Window, + window: glutin::ContextWrapper, cursor_grabbed: bool, pub pan_sensitivity: u32, pub zoom_sensitivity: u32, @@ -564,9 +565,26 @@ impl Window { false, ); - let window = win_builder.build(&event_loop).unwrap(); + let (window, device, factory, win_color_view, win_depth_view) = + glutin::ContextBuilder::new() + .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 3))) + .with_vsync(false) + .with_gfx_color_depth::() + .build_windowed(win_builder, &event_loop) + .map_err(|err| Error::BackendError(Box::new(err)))? + .init_gfx::(); - let renderer = Renderer::new(&window, settings.graphics.render_mode.clone())?; + let vendor = device.get_info().platform_name.vendor; + let renderer = device.get_info().platform_name.renderer; + let opengl_version = device.get_info().version; + let glsl_version = device.get_info().shading_language; + info!( + ?vendor, + ?renderer, + ?opengl_version, + ?glsl_version, + "selected graphics device" + ); let keypress_map = HashMap::new(); @@ -599,9 +617,9 @@ impl Window { channel::Receiver, ) = channel::unbounded::(); - let scale_factor = window.scale_factor(); + let scale_factor = window.window().scale_factor(); - let key_layout = match KeyLayout::new_from_window(&window) { + let key_layout = match KeyLayout::new_from_window(window.window()) { Ok(kl) => Some(kl), Err(err) => { warn!( @@ -614,7 +632,13 @@ impl Window { }; let mut this = Self { - renderer, + renderer: Renderer::new( + device, + factory, + win_color_view, + win_depth_view, + settings.graphics.render_mode.clone(), + )?, window, cursor_grabbed: false, pan_sensitivity: settings.gameplay.pan_sensitivity, @@ -663,7 +687,6 @@ impl Window { } pub fn fetch_events(&mut self) -> Vec { - span!(_guard, "fetch_events", "Window::fetch_events"); // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { let logical_size = self.logical_size(); @@ -928,15 +951,11 @@ impl Window { match event { WindowEvent::CloseRequested => self.events.push(Event::Close), - WindowEvent::Resized(_) => { - // We don't use the event provided size because since this event - // more could have happened making the value wrong so we query - // directly from the window, this prevents some errors - let physical = self.window.inner_size(); - - self.renderer - .on_resize(Vec2::new(physical.width, physical.height)) - .unwrap(); + WindowEvent::Resized(physical) => { + let (mut color_view, mut depth_view) = self.renderer.win_views_mut(); + self.window.resize(physical); + self.window.update_gfx(&mut color_view, &mut depth_view); + self.renderer.on_resize().unwrap(); // TODO: update users of this event with the fact that it is now the physical // size let winit::dpi::PhysicalSize { width, height } = physical; @@ -944,7 +963,6 @@ impl Window { .push(Event::Resize(Vec2::new(width as u32, height as u32))); }, WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - // TODO: is window resized event emitted? or do we need to handle that here? self.scale_factor = scale_factor; self.events.push(Event::ScaleFactorChanged(scale_factor)); }, @@ -1081,24 +1099,32 @@ impl Window { /// Moves cursor by an offset pub fn offset_cursor(&self, d: Vec2) { if d != Vec2::zero() { - if let Err(err) = self - .window - .set_cursor_position(winit::dpi::LogicalPosition::new( - d.x as f64 + self.cursor_position.x, - d.y as f64 + self.cursor_position.y, - )) + if let Err(err) = + self.window + .window() + .set_cursor_position(winit::dpi::LogicalPosition::new( + d.x as f64 + self.cursor_position.x, + d.y as f64 + self.cursor_position.y, + )) { error!("Error setting cursor position: {:?}", err); } } } + pub fn swap_buffers(&self) -> Result<(), Error> { + span!(_guard, "swap_buffers", "Window::swap_buffers"); + self.window + .swap_buffers() + .map_err(|err| Error::BackendError(Box::new(err))) + } + pub fn is_cursor_grabbed(&self) -> bool { self.cursor_grabbed } pub fn grab_cursor(&mut self, grab: bool) { self.cursor_grabbed = grab; - self.window.set_cursor_visible(!grab); - let _ = self.window.set_cursor_grab(grab); + self.window.window().set_cursor_visible(!grab); + let _ = self.window.window().set_cursor_grab(grab); } /// Moves mouse cursor to center of screen @@ -1106,12 +1132,13 @@ impl Window { pub fn center_cursor(&self) { let dimensions: Vec2 = self.logical_size(); - if let Err(err) = self - .window - .set_cursor_position(winit::dpi::PhysicalPosition::new( - dimensions[0] / (2_f64), - dimensions[1] / (2_f64), - )) + if let Err(err) = + self.window + .window() + .set_cursor_position(winit::dpi::PhysicalPosition::new( + dimensions[0] / (2_f64), + dimensions[1] / (2_f64), + )) { error!("Error centering cursor position: {:?}", err); } @@ -1143,7 +1170,8 @@ impl Window { // the correct resolution already, load that value, otherwise filter it // in this iteration let correct_res = correct_res.unwrap_or_else(|| { - self.window + let window = self.window.window(); + window .current_monitor() .unwrap() .video_modes() @@ -1295,6 +1323,7 @@ impl Window { self .window + .window() .current_monitor().unwrap() .video_modes() // Prefer bit depth over refresh rate @@ -1306,7 +1335,7 @@ impl Window { } pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) { - let window = &self.window; + let window = self.window.window(); self.fullscreen = fullscreen; window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode { FullscreenMode::Exclusive => { @@ -1328,50 +1357,61 @@ impl Window { pub fn logical_size(&self) -> Vec2 { let (w, h) = self .window + .window() .inner_size() - .to_logical::(self.window.scale_factor()) + .to_logical::(self.window.window().scale_factor()) .into(); Vec2::new(w, h) } pub fn set_size(&mut self, new_size: Vec2) { - self.window.set_inner_size(winit::dpi::LogicalSize::new( - new_size.x as f64, - new_size.y as f64, - )); + self.window + .window() + .set_inner_size(glutin::dpi::LogicalSize::new( + new_size.x as f64, + new_size.y as f64, + )); } pub fn send_event(&mut self, event: Event) { self.events.push(event) } pub fn take_screenshot(&mut self, settings: &Settings) { - let sender = self.message_sender.clone(); - let mut path = settings.screenshots_path.clone(); - self.renderer.create_screenshot(move |image| { - use std::time::SystemTime; - // Check if folder exists and create it if it does not - if !path.exists() { - if let Err(e) = std::fs::create_dir_all(&path) { - warn!(?e, "Couldn't create folder for screenshot"); - let _result = - sender.send(String::from("Couldn't create folder for screenshot")); - } - } - path.push(format!( - "screenshot_{}.png", - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0) - )); - // Try to save the image - if let Err(e) = image.into_rgba8().save(&path) { - warn!(?e, "Couldn't save screenshot"); - let _result = sender.send(String::from("Couldn't save screenshot")); - } else { - let _result = - sender.send(format!("Screenshot saved to {}", path.to_string_lossy())); - } - }); + match self.renderer.create_screenshot() { + Ok(img) => { + let mut path = settings.screenshots_path.clone(); + let sender = self.message_sender.clone(); + + let builder = std::thread::Builder::new().name("screenshot".into()); + builder + .spawn(move || { + use std::time::SystemTime; + // Check if folder exists and create it if it does not + if !path.exists() { + if let Err(e) = std::fs::create_dir_all(&path) { + warn!(?e, "Couldn't create folder for screenshot"); + let _result = sender + .send(String::from("Couldn't create folder for screenshot")); + } + } + path.push(format!( + "screenshot_{}.png", + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map(|d| d.as_millis()) + .unwrap_or(0) + )); + if let Err(e) = img.save(&path) { + warn!(?e, "Couldn't save screenshot"); + let _result = sender.send(String::from("Couldn't save screenshot")); + } else { + let _result = sender + .send(format!("Screenshot saved to {}", path.to_string_lossy())); + } + }) + .unwrap(); + }, + Err(e) => error!(?e, "Couldn't create screenshot due to renderer error"), + } } fn is_pressed( @@ -1418,7 +1458,7 @@ impl Window { self.remapping_keybindings = Some(game_input); } - pub fn window(&self) -> &winit::window::Window { &self.window } + pub fn window(&self) -> &winit::window::Window { self.window.window() } pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers }