From 9f62f5f2fd392b3e3e544633045a49c499cff189 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Wed, 18 Oct 2023 17:17:21 -0700 Subject: [PATCH] graphics thread + refactor everything + drop deps + update winit --- Cargo.lock | 817 ++++++++++++----------- Cargo.toml | 5 +- src/compat_worker.rs | 21 + src/framework.rs | 516 --------------- src/graphics.rs | 993 ++++++++++++++++++++++++++++ src/graphics_worker.rs | 62 ++ src/integer.rs | 5 + src/main.rs | 1421 +++------------------------------------- src/physics.rs | 145 +--- src/physics_worker.rs | 133 ++++ src/settings.rs | 7 +- src/setup.rs | 300 +++++++++ src/window.rs | 243 +++++++ src/worker.rs | 143 +++- 14 files changed, 2460 insertions(+), 2351 deletions(-) create mode 100644 src/compat_worker.rs delete mode 100644 src/framework.rs create mode 100644 src/graphics.rs create mode 100644 src/graphics_worker.rs create mode 100644 src/physics_worker.rs create mode 100644 src/setup.rs create mode 100644 src/window.rs diff --git a/Cargo.lock b/Cargo.lock index c6348cd..43b99d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.5" @@ -55,20 +68,23 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" dependencies = [ "android-properties", - "bitflags 1.3.2", + "bitflags 2.4.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", "ndk", "ndk-context", "ndk-sys", - "num_enum 0.6.1", + "num_enum", + "thiserror", ] [[package]] @@ -98,6 +114,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -108,33 +130,10 @@ dependencies = [ ] [[package]] -name = "async-executor" -version = "1.5.1" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" @@ -221,21 +220,21 @@ dependencies = [ [[package]] name = "block-sys" -version = "0.1.0-beta.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" dependencies = [ "objc-sys", ] [[package]] name = "block2" -version = "0.2.0-alpha.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" dependencies = [ "block-sys", - "objc2-encode", + "objc2", ] [[package]] @@ -261,7 +260,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] @@ -271,17 +270,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "calloop" -version = "0.10.6" +name = "bytes" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "calloop" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "log", - "nix 0.25.1", - "slotmap", + "polling", + "rustix", + "slab", "thiserror", - "vec_map", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", ] [[package]] @@ -294,6 +311,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -323,10 +346,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" [[package]] -name = "concurrent-queue" -version = "2.2.0" +name = "combine" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] @@ -361,14 +394,14 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", ] @@ -383,15 +416,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -411,6 +435,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "cursor-icon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740bb192a8e2d1350119916954f4409ee7f62f149b536911eeb78ba5a20526bf" + [[package]] name = "d3d12" version = "0.7.0" @@ -474,19 +504,6 @@ dependencies = [ "num-traits 0.1.43", ] -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -495,68 +512,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -564,7 +527,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -575,48 +538,15 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -627,6 +557,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -720,7 +660,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -744,12 +684,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "hexf-parse" version = "0.2.1" @@ -757,10 +691,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] -name = "humantime" -version = "2.1.0" +name = "icrate" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] [[package]] name = "indexmap" @@ -783,26 +722,19 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" +name = "jni" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ + "cesu8", "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -860,7 +792,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.29", + "syn", ] [[package]] @@ -871,9 +803,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libloading" @@ -897,9 +829,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -954,18 +886,18 @@ checksum = "f478948fd84d9f8e86967bf432640e46adfb5a4bd4f14ef7e864ab38220534ae" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] @@ -979,7 +911,7 @@ dependencies = [ "bitflags 2.4.0", "block", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "log", "objc", "paste", @@ -998,19 +930,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", ] [[package]] @@ -1035,15 +954,17 @@ dependencies = [ [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "jni-sys", + "log", "ndk-sys", - "num_enum 0.5.11", - "raw-window-handle", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "thiserror", ] @@ -1055,18 +976,18 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] [[package]] name = "nix" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -1074,19 +995,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", -] - [[package]] name = "nom" version = "7.1.3" @@ -1117,44 +1025,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive 0.6.1", + "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "num_enum_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.29", + "syn", ] [[package]] @@ -1175,29 +1062,25 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.2.0-beta.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" dependencies = [ - "block2", "objc-sys", "objc2-encode", ] [[package]] name = "objc2-encode" -version = "2.0.0-pre.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" -dependencies = [ - "objc-sys", -] +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" [[package]] name = "objc_exception" @@ -1241,12 +1124,6 @@ dependencies = [ "ttf-parser", ] -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1295,16 +1172,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] -name = "png" -version = "0.17.10" +name = "polling" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "62a79e457c9898100b4298d57d69ec53d06f9a6ed352431ce5f377e082d2e846" dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.48.0", ] [[package]] @@ -1354,7 +1232,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74c55e9e629af5298a40e0fa106435b2da30484c4ec76b41d19bc4d00dd8b903" dependencies = [ "quote", - "syn 2.0.29", + "syn", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", ] [[package]] @@ -1408,6 +1295,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rbx_binary" version = "0.7.1" @@ -1565,9 +1458,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.10" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ "bitflags 2.4.0", "errno", @@ -1576,6 +1469,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1590,9 +1492,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.5.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +checksum = "1729a30a469de249c6effc17ec8d039b0aa29b3af79b819b7f51cb6ab8046a90" dependencies = [ "ab_glyph", "log", @@ -1618,15 +1520,9 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "slab" version = "0.4.9" @@ -1653,21 +1549,36 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithay-client-toolkit" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix", + "thiserror", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", ] [[package]] @@ -1690,14 +1601,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" name = "strafe-client" version = "0.8.0" dependencies = [ - "async-executor", "bytemuck", "configparser", "ddsfile", - "env_logger", "glam", "lazy-regex", - "log", "obj", "parking_lot", "pollster", @@ -1723,20 +1631,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -1769,28 +1666,28 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", ] [[package]] name = "tiny-skia" -version = "0.8.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" dependencies = [ "arrayref", "arrayvec", "bytemuck", "cfg-if", - "png", + "log", "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.8.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" dependencies = [ "arrayref", "bytemuck", @@ -1814,6 +1711,22 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + [[package]] name = "ttf-parser" version = "0.19.1" @@ -1832,6 +1745,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" @@ -1844,12 +1763,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -1857,10 +1770,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "waker-fn" -version = "1.1.0" +name = "walkdir" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "wasi" @@ -1889,7 +1806,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn", "wasm-bindgen-shared", ] @@ -1923,7 +1840,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1935,75 +1852,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" dependencies = [ - "bitflags 1.3.2", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "nix", "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", "smallvec", "wayland-sys", ] [[package]] -name = "wayland-cursor" -version = "0.29.5" +name = "wayland-client" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" dependencies = [ - "nix 0.24.3", + "bitflags 2.4.0", + "nix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" +dependencies = [ + "nix", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", + "wayland-backend", "wayland-client", - "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" dependencies = [ "proc-macro2", + "quick-xml", "quote", - "xml-rs", ] [[package]] name = "wayland-sys" -version = "0.29.5" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", - "lazy_static", + "log", + "once_cell", "pkg-config", ] @@ -2017,6 +1970,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wgpu" version = "0.17.0" @@ -2030,7 +1993,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -2055,7 +2018,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "rustc-hash", "smallvec", "thiserror", @@ -2094,7 +2057,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.5.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -2147,6 +2110,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2296,37 +2268,51 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winit" -version = "0.28.6" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196" +checksum = "b829f75d02fe1e225b97c91a04c326900147a50234d1141a1cbe215ce8798c11" dependencies = [ + "ahash 0.8.5", "android-activity", - "bitflags 1.3.2", + "atomic-waker", + "bitflags 2.4.0", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", "core-graphics", - "dispatch", - "instant", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2", "ndk", + "ndk-sys", "objc2", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "redox_syscall", + "rustix", "sctk-adwaita", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] @@ -2349,6 +2335,32 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.7.4", + "nix", + "once_cell", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" +dependencies = [ + "nix", +] + [[package]] name = "xcursor" version = "0.3.4" @@ -2358,8 +2370,47 @@ dependencies = [ "nom", ] +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +dependencies = [ + "bitflags 2.4.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "xml-rs" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" + +[[package]] +name = "zerocopy" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c19fae0c8a9efc6a8281f2e623db8af1db9e57852e04cde3e754dd2dc29340f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc56589e9ddd1f1c28d4b4b5c773ce232910a6bb67a70133d61c9e347585efe9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 00031aa..3156a7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-executor = "1.5.1" bytemuck = { version = "1.13.1", features = ["derive"] } configparser = "3.0.2" ddsfile = "0.5.1" -env_logger = "0.10.0" glam = "0.24.1" lazy-regex = "3.0.2" -log = "0.4.20" obj = "0.10.2" parking_lot = "0.12.1" pollster = "0.3.0" @@ -22,7 +19,7 @@ rbx_dom_weak = "2.5.0" rbx_reflection_database = "0.2.7" rbx_xml = "0.13.1" wgpu = "0.17.0" -winit = "0.28.6" +winit = { version = "0.29.2", features = ["rwh_05"] } #[profile.release] #lto = true diff --git a/src/compat_worker.rs b/src/compat_worker.rs new file mode 100644 index 0000000..d41ea3e --- /dev/null +++ b/src/compat_worker.rs @@ -0,0 +1,21 @@ +pub type QNWorker<'a,Task>=CompatNWorker<'a,Task>; +pub type INWorker<'a,Task>=CompatNWorker<'a,Task>; + +pub struct CompatNWorker<'a,Task>{ + data:std::marker::PhantomData, + f:Box, +} + +impl<'a,Task> CompatNWorker<'a,Task>{ + pub fn new(f:impl FnMut(Task)+'a)->CompatNWorker<'a,Task>{ + Self{ + data:std::marker::PhantomData, + f:Box::new(f), + } + } + + pub fn send(&mut self,task:Task)->Result<(),()>{ + (self.f)(task); + Ok(()) + } +} diff --git a/src/framework.rs b/src/framework.rs deleted file mode 100644 index f7acef1..0000000 --- a/src/framework.rs +++ /dev/null @@ -1,516 +0,0 @@ -use std::future::Future; -#[cfg(target_arch = "wasm32")] -use std::str::FromStr; -#[cfg(target_arch = "wasm32")] -use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas}; -use winit::{ - event::{self, WindowEvent, DeviceEvent}, - event_loop::{ControlFlow, EventLoop}, -}; - -#[allow(dead_code)] -pub fn cast_slice(data: &[T]) -> &[u8] { - use std::{mem::size_of, slice::from_raw_parts}; - - unsafe { from_raw_parts(data.as_ptr() as *const u8, data.len() * size_of::()) } -} - -#[allow(dead_code)] -pub enum ShaderStage { - Vertex, - Fragment, - Compute, -} - -pub trait Example: 'static + Sized { - fn optional_features() -> wgpu::Features { - wgpu::Features::empty() - } - fn required_features() -> wgpu::Features { - wgpu::Features::empty() - } - fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { - wgpu::DownlevelCapabilities { - flags: wgpu::DownlevelFlags::empty(), - shader_model: wgpu::ShaderModel::Sm5, - ..wgpu::DownlevelCapabilities::default() - } - } - fn required_limits() -> wgpu::Limits { - wgpu::Limits::downlevel_webgl2_defaults() // These downlevel limits will allow the code to run on all possible hardware - } - fn init( - config: &wgpu::SurfaceConfiguration, - adapter: &wgpu::Adapter, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Self; - fn resize( - &mut self, - config: &wgpu::SurfaceConfiguration, - device: &wgpu::Device, - queue: &wgpu::Queue, - ); - fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: WindowEvent); - fn device_event(&mut self, window: &winit::window::Window, event: DeviceEvent); - fn load_file(&mut self, path:std::path::PathBuf, device: &wgpu::Device, queue: &wgpu::Queue); - fn render( - &mut self, - view: &wgpu::TextureView, - device: &wgpu::Device, - queue: &wgpu::Queue, - spawner: &Spawner, - ); -} - -struct Setup { - window: winit::window::Window, - event_loop: EventLoop<()>, - instance: wgpu::Instance, - size: winit::dpi::PhysicalSize, - surface: wgpu::Surface, - adapter: wgpu::Adapter, - device: wgpu::Device, - queue: wgpu::Queue, - #[cfg(target_arch = "wasm32")] - offscreen_canvas_setup: Option, -} - -#[cfg(target_arch = "wasm32")] -struct OffscreenCanvasSetup { - offscreen_canvas: OffscreenCanvas, - bitmap_renderer: ImageBitmapRenderingContext, -} - -async fn setup(title: &str) -> Setup { - #[cfg(not(target_arch = "wasm32"))] - { - env_logger::init(); - }; - - let event_loop = EventLoop::new(); - let mut builder = winit::window::WindowBuilder::new(); - builder = builder.with_title(title); - #[cfg(windows_OFF)] // TODO - { - use winit::platform::windows::WindowBuilderExtWindows; - builder = builder.with_no_redirection_bitmap(true); - } - let window = builder.build(&event_loop).unwrap(); - - #[cfg(target_arch = "wasm32")] - { - use winit::platform::web::WindowExtWebSys; - let query_string = web_sys::window().unwrap().location().search().unwrap(); - let level: log::Level = parse_url_query_string(&query_string, "RUST_LOG") - .and_then(|x| x.parse().ok()) - .unwrap_or(log::Level::Error); - console_log::init_with_level(level).expect("could not initialize logger"); - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - // On wasm, append the canvas to the document body - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.body()) - .and_then(|body| { - body.append_child(&web_sys::Element::from(window.canvas())) - .ok() - }) - .expect("couldn't append canvas to document body"); - } - - #[cfg(target_arch = "wasm32")] - let mut offscreen_canvas_setup: Option = None; - #[cfg(target_arch = "wasm32")] - { - use wasm_bindgen::JsCast; - use winit::platform::web::WindowExtWebSys; - - let query_string = web_sys::window().unwrap().location().search().unwrap(); - if let Some(offscreen_canvas_param) = - parse_url_query_string(&query_string, "offscreen_canvas") - { - if FromStr::from_str(offscreen_canvas_param) == Ok(true) { - log::info!("Creating OffscreenCanvasSetup"); - - let offscreen_canvas = - OffscreenCanvas::new(1024, 768).expect("couldn't create OffscreenCanvas"); - - let bitmap_renderer = window - .canvas() - .get_context("bitmaprenderer") - .expect("couldn't create ImageBitmapRenderingContext (Result)") - .expect("couldn't create ImageBitmapRenderingContext (Option)") - .dyn_into::() - .expect("couldn't convert into ImageBitmapRenderingContext"); - - offscreen_canvas_setup = Some(OffscreenCanvasSetup { - offscreen_canvas, - bitmap_renderer, - }) - } - } - }; - - log::info!("Initializing the surface..."); - - let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); - let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); - - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends, - dx12_shader_compiler, - }); - let (size, surface) = unsafe { - let size = window.inner_size(); - - #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] - let surface = instance.create_surface(&window).unwrap(); - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - let surface = { - if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup { - log::info!("Creating surface from OffscreenCanvas"); - instance.create_surface_from_offscreen_canvas( - offscreen_canvas_setup.offscreen_canvas.clone(), - ) - } else { - instance.create_surface(&window) - } - } - .unwrap(); - - (size, surface) - }; - - let adapter; - - let optional_features = E::optional_features(); - let required_features = E::required_features(); - - //no helper function smh gotta write it myself - let adapters = instance.enumerate_adapters(backends); - - let mut chosen_adapter = None; - let mut chosen_adapter_score=0; - for adapter in adapters { - if !adapter.is_surface_supported(&surface) { - continue; - } - - let score=match adapter.get_info().device_type{ - wgpu::DeviceType::IntegratedGpu=>3, - wgpu::DeviceType::DiscreteGpu=>4, - wgpu::DeviceType::VirtualGpu=>2, - wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1, - }; - - let adapter_features = adapter.features(); - if chosen_adapter_score= required_downlevel_capabilities.shader_model, - "Adapter does not support the minimum shader model required to run this example: {:?}", - required_downlevel_capabilities.shader_model - ); - assert!( - downlevel_capabilities - .flags - .contains(required_downlevel_capabilities.flags), - "Adapter does not support the downlevel capabilities required to run this example: {:?}", - required_downlevel_capabilities.flags - downlevel_capabilities.flags - ); - - // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface. - let needed_limits = E::required_limits().using_resolution(adapter.limits()); - - let trace_dir = std::env::var("WGPU_TRACE"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: (optional_features & adapter.features()) | required_features, - limits: needed_limits, - }, - trace_dir.ok().as_ref().map(std::path::Path::new), - ) - .await - .expect("Unable to find a suitable GPU adapter!"); - - Setup { - window, - event_loop, - instance, - size, - surface, - adapter, - device, - queue, - #[cfg(target_arch = "wasm32")] - offscreen_canvas_setup, - } -} - -fn start( - #[cfg(not(target_arch = "wasm32"))] Setup { - window, - event_loop, - instance, - size, - surface, - adapter, - device, - queue, - }: Setup, - #[cfg(target_arch = "wasm32")] Setup { - window, - event_loop, - instance, - size, - surface, - adapter, - device, - queue, - offscreen_canvas_setup, - }: Setup, -) { - let spawner = Spawner::new(); - let mut config = surface - .get_default_config(&adapter, size.width, size.height) - .expect("Surface isn't supported by the adapter."); - let surface_view_format = config.format.add_srgb_suffix(); - config.view_formats.push(surface_view_format); - surface.configure(&device, &config); - - log::info!("Initializing the example..."); - let mut example = E::init(&config, &adapter, &device, &queue); - - log::info!("Entering render loop..."); - event_loop.run(move |event, _, control_flow| { - let _ = (&instance, &adapter); // force ownership by the closure - *control_flow = if cfg!(feature = "metal-auto-capture") { - ControlFlow::Exit - } else { - ControlFlow::Poll - }; - match event { - event::Event::RedrawEventsCleared => { - #[cfg(not(target_arch = "wasm32"))] - spawner.run_until_stalled(); - - window.request_redraw(); - } - event::Event::WindowEvent { - event: - WindowEvent::Resized(size) - | WindowEvent::ScaleFactorChanged { - new_inner_size: &mut size, - .. - }, - .. - } => { - // Once winit is fixed, the detection conditions here can be removed. - // https://github.com/rust-windowing/winit/issues/2876 - let max_dimension = adapter.limits().max_texture_dimension_2d; - if size.width > max_dimension || size.height > max_dimension { - log::warn!( - "The resizing size {:?} exceeds the limit of {}.", - size, - max_dimension - ); - } else { - log::info!("Resizing to {:?}", size); - config.width = size.width.max(1); - config.height = size.height.max(1); - example.resize(&config, &device, &queue); - surface.configure(&device, &config); - } - } - event::Event::WindowEvent { event, .. } => match event { - WindowEvent::KeyboardInput { - input: - event::KeyboardInput { - virtual_keycode: Some(event::VirtualKeyCode::Escape), - state: event::ElementState::Pressed, - .. - }, - .. - } - | WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - #[cfg(not(target_arch = "wasm32"))] - WindowEvent::KeyboardInput { - input: - event::KeyboardInput { - virtual_keycode: Some(event::VirtualKeyCode::Scroll), - state: event::ElementState::Pressed, - .. - }, - .. - } => { - println!("{:#?}", instance.generate_report()); - } - _ => { - example.update(&window,&device,&queue,event); - } - }, - event::Event::DeviceEvent { - event, - .. - } => { - example.device_event(&window,event); - }, - event::Event::RedrawRequested(_) => { - - let frame = match surface.get_current_texture() { - Ok(frame) => frame, - Err(_) => { - surface.configure(&device, &config); - surface - .get_current_texture() - .expect("Failed to acquire next surface texture!") - } - }; - let view = frame.texture.create_view(&wgpu::TextureViewDescriptor { - format: Some(surface_view_format), - ..wgpu::TextureViewDescriptor::default() - }); - - example.render(&view, &device, &queue, &spawner); - - frame.present(); - - #[cfg(target_arch = "wasm32")] - { - if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup { - let image_bitmap = offscreen_canvas_setup - .offscreen_canvas - .transfer_to_image_bitmap() - .expect("couldn't transfer offscreen canvas to image bitmap."); - offscreen_canvas_setup - .bitmap_renderer - .transfer_from_image_bitmap(&image_bitmap); - - log::info!("Transferring OffscreenCanvas to ImageBitmapRenderer"); - } - } - } - _ => {} - } - }); -} - -#[cfg(not(target_arch = "wasm32"))] -pub struct Spawner<'a> { - executor: async_executor::LocalExecutor<'a>, -} - -#[cfg(not(target_arch = "wasm32"))] -impl<'a> Spawner<'a> { - fn new() -> Self { - Self { - executor: async_executor::LocalExecutor::new(), - } - } - - #[allow(dead_code)] - pub fn spawn_local(&self, future: impl Future + 'a) { - self.executor.spawn(future).detach(); - } - - fn run_until_stalled(&self) { - while self.executor.try_tick() {} - } -} - -#[cfg(target_arch = "wasm32")] -pub struct Spawner {} - -#[cfg(target_arch = "wasm32")] -impl Spawner { - fn new() -> Self { - Self {} - } - - #[allow(dead_code)] - pub fn spawn_local(&self, future: impl Future + 'static) { - wasm_bindgen_futures::spawn_local(future); - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn run(title: &str) { - let setup = pollster::block_on(setup::(title)); - start::(setup); -} - -#[cfg(target_arch = "wasm32")] -pub fn run(title: &str) { - use wasm_bindgen::prelude::*; - - let title = title.to_owned(); - wasm_bindgen_futures::spawn_local(async move { - let setup = setup::(&title).await; - let start_closure = Closure::once_into_js(move || start::(setup)); - - // make sure to handle JS exceptions thrown inside start. - // Otherwise wasm_bindgen_futures Queue would break and never handle any tasks again. - // This is required, because winit uses JS exception for control flow to escape from `run`. - if let Err(error) = call_catch(&start_closure) { - let is_control_flow_exception = error.dyn_ref::().map_or(false, |e| { - e.message().includes("Using exceptions for control flow", 0) - }); - - if !is_control_flow_exception { - web_sys::console::error_1(&error); - } - } - - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen(catch, js_namespace = Function, js_name = "prototype.call.call")] - fn call_catch(this: &JsValue) -> Result<(), JsValue>; - } - }); -} - -#[cfg(target_arch = "wasm32")] -/// Parse the query string as returned by `web_sys::window()?.location().search()?` and get a -/// specific key out of it. -pub fn parse_url_query_string<'a>(query: &'a str, search_key: &str) -> Option<&'a str> { - let query_string = query.strip_prefix('?')?; - - for pair in query_string.split('&') { - let mut pair = pair.split('='); - let key = pair.next()?; - let value = pair.next()?; - - if key == search_key { - return Some(value); - } - } - - None -} - -// This allows treating the framework as a standalone example, -// thus avoiding listing the example names in `Cargo.toml`. -#[allow(dead_code)] -fn main() {} diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..d993258 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,993 @@ +use std::borrow::Cow; +use wgpu::{util::DeviceExt,AstcBlock,AstcChannel}; +use crate::model_graphics::{GraphicsVertex,ModelGraphicsColor4,ModelGraphicsInstance,ModelGraphicsSingleTexture,IndexedModelGraphicsSingleTexture,IndexedGroupFixedTexture}; + +#[derive(Clone)] +pub struct ModelUpdate{ + transform:Option, + color:Option, +} + +struct Entity { + index_count: u32, + index_buf: wgpu::Buffer, +} + +struct ModelGraphics { + instances: Vec, + vertex_buf: wgpu::Buffer, + entities: Vec, + bind_group: wgpu::BindGroup, + model_buf: wgpu::Buffer, +} + +pub struct GraphicsSamplers{ + repeat: wgpu::Sampler, +} + +pub struct GraphicsBindGroupLayouts{ + model: wgpu::BindGroupLayout, +} + +pub struct GraphicsBindGroups { + camera: wgpu::BindGroup, + skybox_texture: wgpu::BindGroup, +} + +pub struct GraphicsPipelines{ + skybox: wgpu::RenderPipeline, + model: wgpu::RenderPipeline, +} + +pub struct GraphicsCamera{ + screen_size: glam::UVec2, + fov: glam::Vec2,//slope + //camera angles and such are extrapolated and passed in every time +} + +#[inline] +fn perspective_rh(fov_x_slope: f32, fov_y_slope: f32, z_near: f32, z_far: f32) -> glam::Mat4 { + //glam_assert!(z_near > 0.0 && z_far > 0.0); + let r = z_far / (z_near - z_far); + glam::Mat4::from_cols( + glam::Vec4::new(1.0/fov_x_slope, 0.0, 0.0, 0.0), + glam::Vec4::new(0.0, 1.0/fov_y_slope, 0.0, 0.0), + glam::Vec4::new(0.0, 0.0, r, -1.0), + glam::Vec4::new(0.0, 0.0, r * z_near, 0.0), + ) +} +impl GraphicsCamera{ + pub fn new(screen_size:glam::UVec2,fov:glam::Vec2)->Self{ + Self{ + screen_size, + fov, + } + } + pub fn proj(&self)->glam::Mat4{ + perspective_rh(self.fov.x, self.fov.y, 0.5, 2000.0) + } + pub fn world(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{ + //f32 good enough for view matrix + glam::Mat4::from_translation(pos) * glam::Mat4::from_euler(glam::EulerRot::YXZ, angles.x, angles.y, 0f32) + } + + pub fn to_uniform_data(&self,(pos,angles): (glam::Vec3,glam::Vec2)) -> [f32; 16 * 4] { + let proj=self.proj(); + let proj_inv = proj.inverse(); + let view_inv=self.world(pos,angles); + let view=view_inv.inverse(); + + let mut raw = [0f32; 16 * 4]; + raw[..16].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj)[..]); + raw[16..32].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj_inv)[..]); + raw[32..48].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view)[..]); + raw[48..64].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view_inv)[..]); + raw + } +} +impl std::default::Default for GraphicsCamera{ + fn default()->Self{ + Self{ + screen_size:glam::UVec2::ONE, + fov:glam::Vec2::ONE, + } + } +} + +pub struct GraphicsState{ + pipelines: GraphicsPipelines, + bind_groups: GraphicsBindGroups, + bind_group_layouts: GraphicsBindGroupLayouts, + samplers: GraphicsSamplers, + camera:GraphicsCamera, + camera_buf: wgpu::Buffer, + temp_squid_texture_view: wgpu::TextureView, + models: Vec, + depth_view: wgpu::TextureView, + staging_belt: wgpu::util::StagingBelt, +} + +impl GraphicsState{ + const DEPTH_FORMAT: wgpu::TextureFormat=wgpu::TextureFormat::Depth24Plus; + fn create_depth_texture( + config: &wgpu::SurfaceConfiguration, + device: &wgpu::Device, + ) -> wgpu::TextureView { + let depth_texture=device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + label: None, + view_formats: &[], + }); + + depth_texture.create_view(&wgpu::TextureViewDescriptor::default()) + } + pub fn clear(&mut self){ + self.models.clear(); + } + pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ + self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); + } + pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,indexed_models:crate::model::IndexedModelInstances){ + //generate texture view per texture + + //idk how to do this gooder lol + let mut double_map=std::collections::HashMap::::new(); + let mut texture_loading_threads=Vec::new(); + let num_textures=indexed_models.textures.len(); + for (i,texture_id) in indexed_models.textures.into_iter().enumerate(){ + if let Ok(mut file) = std::fs::File::open(std::path::Path::new(&format!("textures/{}.dds",texture_id))){ + double_map.insert(i as u32, texture_loading_threads.len() as u32); + texture_loading_threads.push((texture_id,std::thread::spawn(move ||{ + ddsfile::Dds::read(&mut file).unwrap() + }))); + } + } + + let texture_views:Vec=texture_loading_threads.into_iter().map(|(texture_id,thread)|{ + let image=thread.join().unwrap(); + + let (mut width,mut height)=(image.get_width(),image.get_height()); + + let format=match image.header10.unwrap().dxgi_format{ + ddsfile::DxgiFormat::R8G8B8A8_UNorm_sRGB => wgpu::TextureFormat::Rgba8UnormSrgb, + ddsfile::DxgiFormat::BC7_UNorm_sRGB => { + //floor(w,4), should be ceil(w,4) + width=width/4*4; + height=height/4*4; + wgpu::TextureFormat::Bc7RgbaUnormSrgb + }, + other=>panic!("unsupported format {:?}",other), + }; + + let size = wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }; + + let layer_size = wgpu::Extent3d { + depth_or_array_layers: 1, + ..size + }; + let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); + + let texture = device.create_texture_with_data( + queue, + &wgpu::TextureDescriptor { + size, + mip_level_count: max_mips, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: Some(format!("Texture{}",texture_id).as_str()), + view_formats: &[], + }, + &image.data, + ); + texture.create_view(&wgpu::TextureViewDescriptor { + label: Some(format!("Texture{} View",texture_id).as_str()), + dimension: Some(wgpu::TextureViewDimension::D2), + ..wgpu::TextureViewDescriptor::default() + }) + }).collect(); + + //split groups with different textures into separate models + //the models received here are supposed to be tightly packed, i.e. no code needs to check if two models are using the same groups. + let indexed_models_len=indexed_models.models.len(); + let mut unique_texture_models=Vec::with_capacity(indexed_models_len); + for model in indexed_models.models.into_iter(){ + //convert ModelInstance into ModelGraphicsInstance + let instances:Vec=model.instances.into_iter().filter_map(|instance|{ + if instance.color.w==0.0{ + None + }else{ + Some(ModelGraphicsInstance{ + transform: instance.transform.into(), + normal_transform: Into::::into(instance.transform.matrix3).inverse().transpose(), + color:ModelGraphicsColor4::from(instance.color), + }) + } + }).collect(); + //skip pushing a model if all instances are invisible + if instances.len()==0{ + continue; + } + //check each group, if it's using a new texture then make a new clone of the model + let id=unique_texture_models.len(); + let mut unique_textures=Vec::new(); + for group in model.groups.into_iter(){ + //ignore zero copy optimization for now + let texture_index=if let Some(texture_index)=unique_textures.iter().position(|&texture|texture==group.texture){ + texture_index + }else{ + //create new texture_index + let texture_index=unique_textures.len(); + unique_textures.push(group.texture); + unique_texture_models.push(IndexedModelGraphicsSingleTexture{ + unique_pos:model.unique_pos.iter().map(|&v|*Into::::into(v).as_ref()).collect(), + unique_tex:model.unique_tex.iter().map(|v|*v.as_ref()).collect(), + unique_normal:model.unique_normal.iter().map(|&v|*Into::::into(v).as_ref()).collect(), + unique_color:model.unique_color.iter().map(|v|*v.as_ref()).collect(), + unique_vertices:model.unique_vertices.clone(), + texture:group.texture, + groups:Vec::new(), + instances:instances.clone(), + }); + texture_index + }; + unique_texture_models[id+texture_index].groups.push(IndexedGroupFixedTexture{ + polys:group.polys, + }); + } + } + //check every model to see if it's using the same (texture,color) but has few instances, if it is combine it into one model + //1. collect unique instances of texture and color, note model id + //2. for each model id, check if removing it from the pool decreases both the model count and instance count by more than one + //3. transpose all models that stay in the set + + //best plan: benchmark set_bind_group, set_vertex_buffer, set_index_buffer and draw_indexed + //check if the estimated render performance is better by transposing multiple model instances into one model instance + + //for now: just deduplicate single models... + let mut deduplicated_models=Vec::with_capacity(indexed_models_len);//use indexed_models_len because the list will likely get smaller instead of bigger + let mut unique_texture_color=std::collections::HashMap::new();//texture->color->vec![(model_id,instance_id)] + for (model_id,model) in unique_texture_models.iter().enumerate(){ + //for now: filter out models with more than one instance + if 1=model.unique_pos.iter().map(|untransformed_pos|{ + let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array(); + let h=pos.map(|v|bytemuck::cast::(v)); + (if let Some(&pos_id)=pos_id_from.get(&h){ + pos_id + }else{ + let pos_id=unique_pos.len(); + unique_pos.push(pos.clone()); + pos_id_from.insert(h,pos_id); + pos_id + }) as u32 + }).collect(); + let map_tex_id:Vec=model.unique_tex.iter().map(|tex|{ + let h=tex.map(|v|bytemuck::cast::(v)); + (if let Some(&tex_id)=tex_id_from.get(&h){ + tex_id + }else{ + let tex_id=unique_tex.len(); + unique_tex.push(tex.clone()); + tex_id_from.insert(h,tex_id); + tex_id + }) as u32 + }).collect(); + let map_normal_id:Vec=model.unique_normal.iter().map(|untransformed_normal|{ + let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array(); + let h=normal.map(|v|bytemuck::cast::(v)); + (if let Some(&normal_id)=normal_id_from.get(&h){ + normal_id + }else{ + let normal_id=unique_normal.len(); + unique_normal.push(normal.clone()); + normal_id_from.insert(h,normal_id); + normal_id + }) as u32 + }).collect(); + let map_color_id:Vec=model.unique_color.iter().map(|color|{ + let h=color.map(|v|bytemuck::cast::(v)); + (if let Some(&color_id)=color_id_from.get(&h){ + color_id + }else{ + let color_id=unique_color.len(); + unique_color.push(color.clone()); + color_id_from.insert(h,color_id); + color_id + }) as u32 + }).collect(); + //map the indexed vertices onto new indices + //creating the vertex map is slightly different because the vertices are directly hashable + let map_vertex_id:Vec=model.unique_vertices.iter().map(|unmapped_vertex|{ + let vertex=crate::model::IndexedVertex{ + pos:map_pos_id[unmapped_vertex.pos as usize] as u32, + tex:map_tex_id[unmapped_vertex.tex as usize] as u32, + normal:map_normal_id[unmapped_vertex.normal as usize] as u32, + color:map_color_id[unmapped_vertex.color as usize] as u32, + }; + (if let Some(&vertex_id)=vertex_id_from.get(&vertex){ + vertex_id + }else{ + let vertex_id=unique_vertices.len(); + unique_vertices.push(vertex.clone()); + vertex_id_from.insert(vertex,vertex_id); + vertex_id + }) as u32 + }).collect(); + for group in &model.groups{ + for poly in &group.polys{ + polys.push(crate::model::IndexedPolygon{vertices:poly.vertices.iter().map(|&vertex_id|map_vertex_id[vertex_id as usize]).collect()}); + } + } + } + //push model into dedup + deduplicated_models.push(IndexedModelGraphicsSingleTexture{ + unique_pos, + unique_tex, + unique_normal, + unique_color, + unique_vertices, + texture, + groups:vec![IndexedGroupFixedTexture{ + polys + }], + instances:vec![ModelGraphicsInstance{ + transform:glam::Mat4::IDENTITY, + normal_transform:glam::Mat3::IDENTITY, + color + }], + }); + } + } + } + //fill untouched models + for (model_id,model) in unique_texture_models.into_iter().enumerate(){ + if !selected_model_instances.contains(&model_id){ + deduplicated_models.push(model); + } + } + + //de-index models + let deduplicated_models_len=deduplicated_models.len(); + let models:Vec=deduplicated_models.into_iter().map(|model|{ + let mut vertices = Vec::new(); + let mut index_from_vertex = std::collections::HashMap::new();//:: + let mut entities = Vec::new(); + //this mut be combined in a more complex way if the models use different render patterns per group + let mut indices = Vec::new(); + for group in model.groups { + for poly in group.polys { + for end_index in 2..poly.vertices.len() { + for &index in &[0, end_index - 1, end_index] { + let vertex_index = poly.vertices[index]; + if let Some(&i)=index_from_vertex.get(&vertex_index){ + indices.push(i); + }else{ + let i=vertices.len() as u16; + let vertex=&model.unique_vertices[vertex_index as usize]; + vertices.push(GraphicsVertex{ + pos: model.unique_pos[vertex.pos as usize], + tex: model.unique_tex[vertex.tex as usize], + normal: model.unique_normal[vertex.normal as usize], + color:model.unique_color[vertex.color as usize], + }); + index_from_vertex.insert(vertex_index,i); + indices.push(i); + } + } + } + } + } + entities.push(indices); + ModelGraphicsSingleTexture{ + instances:model.instances, + vertices, + entities, + texture:model.texture, + } + }).collect(); + //.into_iter() the modeldata vec so entities can be /moved/ to models.entities + let mut model_count=0; + let mut instance_count=0; + let uniform_buffer_binding_size=crate::setup::required_limits().max_uniform_buffer_binding_size as usize; + let chunk_size=uniform_buffer_binding_size/MODEL_BUFFER_SIZE_BYTES; + self.models.reserve(models.len()); + for model in models.into_iter() { + instance_count+=model.instances.len(); + for instances_chunk in model.instances.rchunks(chunk_size){ + model_count+=1; + let model_uniforms = get_instances_buffer_data(instances_chunk); + let model_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(format!("Model{} Buf",model_count).as_str()), + contents: bytemuck::cast_slice(&model_uniforms), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + let texture_view=match model.texture{ + Some(texture_id)=>{ + match double_map.get(&texture_id){ + Some(&mapped_texture_id)=>&texture_views[mapped_texture_id as usize], + None=>&self.temp_squid_texture_view, + } + }, + None=>&self.temp_squid_texture_view, + }; + let model_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &self.bind_group_layouts.model, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: model_buf.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(texture_view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&self.samplers.repeat), + }, + ], + label: Some(format!("Model{} Bind Group",model_count).as_str()), + }); + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex"), + contents: bytemuck::cast_slice(&model.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + //all of these are being moved here + self.models.push(ModelGraphics{ + instances:instances_chunk.to_vec(), + vertex_buf, + entities: model.entities.iter().map(|indices|{ + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index"), + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::INDEX, + }); + Entity { + index_buf, + index_count: indices.len() as u32, + } + }).collect(), + bind_group: model_bind_group, + model_buf, + }); + } + } + println!("Texture References={}",num_textures); + println!("Textures Loaded={}",texture_views.len()); + println!("Indexed Models={}",indexed_models_len); + println!("Deduplicated Models={}",deduplicated_models_len); + println!("Graphics Objects: {}",self.models.len()); + println!("Graphics Instances: {}",instance_count); + } + + pub fn new( + device:&wgpu::Device, + queue:&wgpu::Queue, + config:&wgpu::SurfaceConfiguration, + )->Self{ + let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }); + let skybox_texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Skybox Texture Bind Group Layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + multisampled: false, + view_dimension: wgpu::TextureViewDimension::Cube, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); + let model_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Model Bind Group Layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); + + let clamp_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("Clamp 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::Linear, + ..Default::default() + }); + let repeat_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("Repeat Sampler"), + address_mode_u: wgpu::AddressMode::Repeat, + address_mode_v: wgpu::AddressMode::Repeat, + address_mode_w: wgpu::AddressMode::Repeat, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + + // Create the render pipeline + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), + }); + + //load textures + let device_features = device.features(); + + let skybox_texture_view={ + let skybox_format = if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ASTC) { + println!("Using ASTC"); + wgpu::TextureFormat::Astc { + block: AstcBlock::B4x4, + channel: AstcChannel::UnormSrgb, + } + } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ETC2) { + println!("Using ETC2"); + wgpu::TextureFormat::Etc2Rgb8UnormSrgb + } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) { + println!("Using BC"); + wgpu::TextureFormat::Bc1RgbaUnormSrgb + } else { + println!("Using plain"); + wgpu::TextureFormat::Bgra8UnormSrgb + }; + + let bytes = match skybox_format { + wgpu::TextureFormat::Astc { + block: AstcBlock::B4x4, + channel: AstcChannel::UnormSrgb, + } => &include_bytes!("../images/astc.dds")[..], + wgpu::TextureFormat::Etc2Rgb8UnormSrgb => &include_bytes!("../images/etc2.dds")[..], + wgpu::TextureFormat::Bc1RgbaUnormSrgb => &include_bytes!("../images/bc1.dds")[..], + wgpu::TextureFormat::Bgra8UnormSrgb => &include_bytes!("../images/bgra.dds")[..], + _ => unreachable!(), + }; + + let skybox_image = ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap(); + + let size = wgpu::Extent3d { + width: skybox_image.get_width(), + height: skybox_image.get_height(), + depth_or_array_layers: 6, + }; + + let layer_size = wgpu::Extent3d { + depth_or_array_layers: 1, + ..size + }; + let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); + + let skybox_texture = device.create_texture_with_data( + queue, + &wgpu::TextureDescriptor { + size, + mip_level_count: max_mips, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: skybox_format, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: Some("Skybox Texture"), + view_formats: &[], + }, + &skybox_image.data, + ); + + skybox_texture.create_view(&wgpu::TextureViewDescriptor { + label: Some("Skybox Texture View"), + dimension: Some(wgpu::TextureViewDimension::Cube), + ..wgpu::TextureViewDescriptor::default() + }) + }; + + //squid + let squid_texture_view={ + let bytes = include_bytes!("../images/squid.dds"); + + let image = ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap(); + + let size = wgpu::Extent3d { + width: image.get_width(), + height: image.get_height(), + depth_or_array_layers: 1, + }; + + let layer_size = wgpu::Extent3d { + depth_or_array_layers: 1, + ..size + }; + let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); + + let texture = device.create_texture_with_data( + queue, + &wgpu::TextureDescriptor { + size, + mip_level_count: max_mips, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bc7RgbaUnorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + label: Some("Squid Texture"), + view_formats: &[], + }, + &image.data, + ); + + texture.create_view(&wgpu::TextureViewDescriptor { + label: Some("Squid Texture View"), + dimension: Some(wgpu::TextureViewDimension::D2), + ..wgpu::TextureViewDescriptor::default() + }) + }; + + let model_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[ + &camera_bind_group_layout, + &skybox_texture_bind_group_layout, + &model_bind_group_layout, + ], + push_constant_ranges: &[], + }); + let sky_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[ + &camera_bind_group_layout, + &skybox_texture_bind_group_layout, + ], + push_constant_ranges: &[], + }); + + // Create the render pipelines + let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Sky Pipeline"), + layout: Some(&sky_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_sky", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_sky", + targets: &[Some(config.view_formats[0].into())], + }), + primitive: wgpu::PrimitiveState { + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: Self::DEPTH_FORMAT, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + let model_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Model Pipeline"), + layout: Some(&model_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_entity_texture", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32x3, 3 => Float32x4], + }], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_entity_texture", + targets: &[Some(config.view_formats[0].into())], + }), + primitive: wgpu::PrimitiveState { + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: Self::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let camera=GraphicsCamera::default(); + let camera_uniforms = camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(glam::IVec2::ZERO,crate::integer::Time::ZERO)); + let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Camera"), + contents: bytemuck::cast_slice(&camera_uniforms), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera_buf.as_entire_binding(), + }, + ], + label: Some("Camera"), + }); + + let skybox_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &skybox_texture_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&skybox_texture_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&clamp_sampler), + }, + ], + label: Some("Sky Texture"), + }); + + let depth_view = Self::create_depth_texture(config, device); + + Self{ + pipelines:GraphicsPipelines{ + skybox:sky_pipeline, + model:model_pipeline + }, + bind_groups:GraphicsBindGroups{ + camera:camera_bind_group, + skybox_texture:skybox_texture_bind_group, + }, + camera, + camera_buf, + models: Vec::new(), + depth_view, + staging_belt: wgpu::util::StagingBelt::new(0x100), + bind_group_layouts: GraphicsBindGroupLayouts { model: model_bind_group_layout }, + samplers: GraphicsSamplers { repeat: repeat_sampler }, + temp_squid_texture_view: squid_texture_view, + } + } + pub fn resize( + &mut self, + device:&wgpu::Device, + config:&wgpu::SurfaceConfiguration, + user_settings:&crate::settings::UserSettings, + ) { + self.depth_view = Self::create_depth_texture(config,device); + self.camera.screen_size=glam::uvec2(config.width, config.height); + self.load_user_settings(user_settings); + } + pub fn render( + &mut self, + view:&wgpu::TextureView, + device:&wgpu::Device, + queue:&wgpu::Queue, + physics_output:crate::physics::PhysicsOutputState, + predicted_time:crate::integer::Time, + mouse_pos:glam::IVec2, + ) { + //TODO: use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + // update rotation + let camera_uniforms = self.camera.to_uniform_data(physics_output.extrapolate(mouse_pos,predicted_time)); + self.staging_belt + .write_buffer( + &mut encoder, + &self.camera_buf, + 0, + wgpu::BufferSize::new((camera_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), + device, + ) + .copy_from_slice(bytemuck::cast_slice(&camera_uniforms)); + //This code only needs to run when the uniforms change + /* + for model in self.models.iter() { + let model_uniforms = get_instances_buffer_data(&model.instances); + self.staging_belt + .write_buffer( + &mut encoder, + &model.model_buf,//description of where data will be written when command is executed + 0,//offset in staging belt? + wgpu::BufferSize::new((model_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), + device, + ) + .copy_from_slice(bytemuck::cast_slice(&model_uniforms)); + } + */ + self.staging_belt.finish(); + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: true, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: false, + }), + stencil_ops: None, + }), + }); + + rpass.set_bind_group(0, &self.bind_groups.camera, &[]); + rpass.set_bind_group(1, &self.bind_groups.skybox_texture, &[]); + + rpass.set_pipeline(&self.pipelines.model); + for model in self.models.iter() { + rpass.set_bind_group(2, &model.bind_group, &[]); + rpass.set_vertex_buffer(0, model.vertex_buf.slice(..)); + + for entity in model.entities.iter() { + rpass.set_index_buffer(entity.index_buf.slice(..), wgpu::IndexFormat::Uint16); + rpass.draw_indexed(0..entity.index_count, 0, 0..model.instances.len() as u32); + } + } + + rpass.set_pipeline(&self.pipelines.skybox); + rpass.draw(0..3, 0..1); + } + + queue.submit(std::iter::once(encoder.finish())); + + self.staging_belt.recall(); + } +} +const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::(); +const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4; +fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec { + let mut raw = Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len()); + for (i,mi) in instances.iter().enumerate(){ + let mut v = raw.split_off(MODEL_BUFFER_SIZE*i); + //model transform + raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]); + //normal transform + raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis)); + raw.extend_from_slice(&[0.0]); + raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis)); + raw.extend_from_slice(&[0.0]); + raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis)); + raw.extend_from_slice(&[0.0]); + //color + raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get())); + raw.append(&mut v); + } + raw +} \ No newline at end of file diff --git a/src/graphics_worker.rs b/src/graphics_worker.rs new file mode 100644 index 0000000..b7ff573 --- /dev/null +++ b/src/graphics_worker.rs @@ -0,0 +1,62 @@ +pub enum Instruction{ + Render(crate::physics::PhysicsOutputState,crate::integer::Time,glam::IVec2), + //UpdateModel(crate::graphics::ModelUpdate), + Resize(winit::dpi::PhysicalSize,crate::settings::UserSettings), + GenerateModels(crate::model::IndexedModelInstances), + ClearModels, +} + +//Ideally the graphics thread worker description is: +/* +WorkerDescription{ + input:Immediate, + output:Realtime(PoolOrdering::Ordered(3)), +} +*/ +//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order + +pub fn new<'a>( + mut graphics:crate::graphics::GraphicsState, + mut config:wgpu::SurfaceConfiguration, + surface:wgpu::Surface, + device:wgpu::Device, + queue:wgpu::Queue, + )->crate::compat_worker::INWorker<'a,Instruction>{ + crate::compat_worker::INWorker::new(move |ins:Instruction|{ + match ins{ + Instruction::GenerateModels(indexed_model_instances)=>{ + graphics.generate_models(&device,&queue,indexed_model_instances); + }, + Instruction::ClearModels=>{ + graphics.clear(); + }, + Instruction::Resize(size,user_settings)=>{ + println!("Resizing to {:?}",size); + config.width=size.width.max(1); + config.height=size.height.max(1); + surface.configure(&device,&config); + graphics.resize(&device,&config,&user_settings); + } + Instruction::Render(physics_output,predicted_time,mouse_pos)=>{ + //this has to go deeper somehow + let frame=match surface.get_current_texture(){ + Ok(frame)=>frame, + Err(_)=>{ + surface.configure(&device,&config); + surface + .get_current_texture() + .expect("Failed to acquire next surface texture!") + } + }; + let view=frame.texture.create_view(&wgpu::TextureViewDescriptor{ + format:Some(config.view_formats[0]), + ..wgpu::TextureViewDescriptor::default() + }); + + graphics.render(&view,&device,&queue,physics_output,predicted_time,mouse_pos); + + frame.present(); + } + } + }) +} \ No newline at end of file diff --git a/src/integer.rs b/src/integer.rs index 56c25e2..2c56016 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -41,6 +41,11 @@ impl std::fmt::Display for Time{ write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0) } } +impl std::default::Default for Time{ + fn default()->Self{ + Self(0) + } +} impl std::ops::Neg for Time{ type Output=Time; #[inline] diff --git a/src/main.rs b/src/main.rs index cb152f6..5c0ef27 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,1335 +1,124 @@ -use std::{borrow::Cow, time::Instant}; -use wgpu::{util::DeviceExt, AstcBlock, AstcChannel}; -use model_graphics::{GraphicsVertex,ModelGraphicsInstance}; -use physics::{InputInstruction, PhysicsInstruction}; -use instruction::{TimedInstruction, InstructionConsumer}; - mod bvh; mod aabb; mod model; -mod model_graphics; -mod zeroes; +mod setup; +mod window; mod worker; +mod zeroes; +mod integer; mod physics; +mod graphics; mod settings; -mod framework; mod primitives; mod instruction; mod load_roblox; -mod integer; - -struct Entity { - index_count: u32, - index_buf: wgpu::Buffer, -} - -struct ModelGraphics { - instances: Vec, - vertex_buf: wgpu::Buffer, - entities: Vec, - bind_group: wgpu::BindGroup, - model_buf: wgpu::Buffer, -} - -pub struct GraphicsSamplers{ - repeat: wgpu::Sampler, -} - -pub struct GraphicsBindGroupLayouts{ - model: wgpu::BindGroupLayout, -} - -pub struct GraphicsBindGroups { - camera: wgpu::BindGroup, - skybox_texture: wgpu::BindGroup, -} - -pub struct GraphicsPipelines{ - skybox: wgpu::RenderPipeline, - model: wgpu::RenderPipeline, -} - -pub struct GraphicsCamera{ - screen_size: glam::UVec2, - fov: glam::Vec2,//slope - //camera angles and such are extrapolated and passed in every time -} - -#[inline] -fn perspective_rh(fov_x_slope: f32, fov_y_slope: f32, z_near: f32, z_far: f32) -> glam::Mat4 { - //glam_assert!(z_near > 0.0 && z_far > 0.0); - let r = z_far / (z_near - z_far); - glam::Mat4::from_cols( - glam::Vec4::new(1.0/fov_x_slope, 0.0, 0.0, 0.0), - glam::Vec4::new(0.0, 1.0/fov_y_slope, 0.0, 0.0), - glam::Vec4::new(0.0, 0.0, r, -1.0), - glam::Vec4::new(0.0, 0.0, r * z_near, 0.0), - ) -} -impl GraphicsCamera{ - pub fn new(screen_size:glam::UVec2,fov:glam::Vec2)->Self{ - Self{ - screen_size, - fov, - } - } - pub fn proj(&self)->glam::Mat4{ - perspective_rh(self.fov.x, self.fov.y, 0.5, 2000.0) - } - pub fn world(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{ - //f32 good enough for view matrix - glam::Mat4::from_translation(pos) * glam::Mat4::from_euler(glam::EulerRot::YXZ, angles.x, angles.y, 0f32) - } - - pub fn to_uniform_data(&self,(pos,angles): (glam::Vec3,glam::Vec2)) -> [f32; 16 * 4] { - let proj=self.proj(); - let proj_inv = proj.inverse(); - let view_inv=self.world(pos,angles); - let view=view_inv.inverse(); - - let mut raw = [0f32; 16 * 4]; - raw[..16].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj)[..]); - raw[16..32].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&proj_inv)[..]); - raw[32..48].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view)[..]); - raw[48..64].copy_from_slice(&AsRef::<[f32; 16]>::as_ref(&view_inv)[..]); - raw - } -} - -pub struct GraphicsState{ - pipelines: GraphicsPipelines, - bind_groups: GraphicsBindGroups, - bind_group_layouts: GraphicsBindGroupLayouts, - samplers: GraphicsSamplers, - camera:GraphicsCamera, - camera_buf: wgpu::Buffer, - temp_squid_texture_view: wgpu::TextureView, - models: Vec, - depth_view: wgpu::TextureView, - staging_belt: wgpu::util::StagingBelt, -} - -impl GraphicsState{ - pub fn clear(&mut self){ - self.models.clear(); - } - pub fn load_user_settings(&mut self,user_settings:&settings::UserSettings){ - self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); - } -} - -pub struct GlobalState{ - start_time: std::time::Instant, - manual_mouse_lock:bool, - mouse:physics::MouseState, - user_settings:settings::UserSettings, - graphics:GraphicsState, - physics_thread:worker::CompatWorker,physics::PhysicsOutputState,Box)->physics::PhysicsOutputState>>, -} - -impl GlobalState{ - const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus; - - fn create_depth_texture( - config: &wgpu::SurfaceConfiguration, - device: &wgpu::Device, - ) -> wgpu::TextureView { - let depth_texture = device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width: config.width, - height: config.height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - label: None, - view_formats: &[], - }); - - depth_texture.create_view(&wgpu::TextureViewDescriptor::default()) - } - - fn generate_model_graphics(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,indexed_models:model::IndexedModelInstances){ - //generate texture view per texture - - //idk how to do this gooder lol - let mut double_map=std::collections::HashMap::::new(); - let mut texture_loading_threads=Vec::new(); - let num_textures=indexed_models.textures.len(); - for (i,texture_id) in indexed_models.textures.into_iter().enumerate(){ - if let Ok(mut file) = std::fs::File::open(std::path::Path::new(&format!("textures/{}.dds",texture_id))){ - double_map.insert(i as u32, texture_loading_threads.len() as u32); - texture_loading_threads.push((texture_id,std::thread::spawn(move ||{ - ddsfile::Dds::read(&mut file).unwrap() - }))); - } - } - - let texture_views:Vec=texture_loading_threads.into_iter().map(|(texture_id,thread)|{ - let image=thread.join().unwrap(); - - let (mut width,mut height)=(image.get_width(),image.get_height()); - - let format=match image.header10.unwrap().dxgi_format{ - ddsfile::DxgiFormat::R8G8B8A8_UNorm_sRGB => wgpu::TextureFormat::Rgba8UnormSrgb, - ddsfile::DxgiFormat::BC7_UNorm_sRGB => { - //floor(w,4), should be ceil(w,4) - width=width/4*4; - height=height/4*4; - wgpu::TextureFormat::Bc7RgbaUnormSrgb - }, - other=>panic!("unsupported format {:?}",other), - }; - - let size = wgpu::Extent3d { - width, - height, - depth_or_array_layers: 1, - }; - - let layer_size = wgpu::Extent3d { - depth_or_array_layers: 1, - ..size - }; - let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); - - let texture = device.create_texture_with_data( - queue, - &wgpu::TextureDescriptor { - size, - mip_level_count: max_mips, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - label: Some(format!("Texture{}",texture_id).as_str()), - view_formats: &[], - }, - &image.data, - ); - texture.create_view(&wgpu::TextureViewDescriptor { - label: Some(format!("Texture{} View",texture_id).as_str()), - dimension: Some(wgpu::TextureViewDimension::D2), - ..wgpu::TextureViewDescriptor::default() - }) - }).collect(); - - //split groups with different textures into separate models - //the models received here are supposed to be tightly packed, i.e. no code needs to check if two models are using the same groups. - let indexed_models_len=indexed_models.models.len(); - let mut unique_texture_models=Vec::with_capacity(indexed_models_len); - for model in indexed_models.models.into_iter(){ - //convert ModelInstance into ModelGraphicsInstance - let instances:Vec=model.instances.into_iter().filter_map(|instance|{ - if instance.color.w==0.0{ - None - }else{ - Some(ModelGraphicsInstance{ - transform: instance.transform.into(), - normal_transform: Into::::into(instance.transform.matrix3).inverse().transpose(), - color:model_graphics::ModelGraphicsColor4::from(instance.color), - }) - } - }).collect(); - //skip pushing a model if all instances are invisible - if instances.len()==0{ - continue; - } - //check each group, if it's using a new texture then make a new clone of the model - let id=unique_texture_models.len(); - let mut unique_textures=Vec::new(); - for group in model.groups.into_iter(){ - //ignore zero copy optimization for now - let texture_index=if let Some(texture_index)=unique_textures.iter().position(|&texture|texture==group.texture){ - texture_index - }else{ - //create new texture_index - let texture_index=unique_textures.len(); - unique_textures.push(group.texture); - unique_texture_models.push(model_graphics::IndexedModelGraphicsSingleTexture{ - unique_pos:model.unique_pos.iter().map(|&v|*Into::::into(v).as_ref()).collect(), - unique_tex:model.unique_tex.iter().map(|v|*v.as_ref()).collect(), - unique_normal:model.unique_normal.iter().map(|&v|*Into::::into(v).as_ref()).collect(), - unique_color:model.unique_color.iter().map(|v|*v.as_ref()).collect(), - unique_vertices:model.unique_vertices.clone(), - texture:group.texture, - groups:Vec::new(), - instances:instances.clone(), - }); - texture_index - }; - unique_texture_models[id+texture_index].groups.push(model_graphics::IndexedGroupFixedTexture{ - polys:group.polys, - }); - } - } - //check every model to see if it's using the same (texture,color) but has few instances, if it is combine it into one model - //1. collect unique instances of texture and color, note model id - //2. for each model id, check if removing it from the pool decreases both the model count and instance count by more than one - //3. transpose all models that stay in the set - - //best plan: benchmark set_bind_group, set_vertex_buffer, set_index_buffer and draw_indexed - //check if the estimated render performance is better by transposing multiple model instances into one model instance - - //for now: just deduplicate single models... - let mut deduplicated_models=Vec::with_capacity(indexed_models_len);//use indexed_models_len because the list will likely get smaller instead of bigger - let mut unique_texture_color=std::collections::HashMap::new();//texture->color->vec![(model_id,instance_id)] - for (model_id,model) in unique_texture_models.iter().enumerate(){ - //for now: filter out models with more than one instance - if 1=model.unique_pos.iter().map(|untransformed_pos|{ - let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array(); - let h=pos.map(|v|bytemuck::cast::(v)); - (if let Some(&pos_id)=pos_id_from.get(&h){ - pos_id - }else{ - let pos_id=unique_pos.len(); - unique_pos.push(pos.clone()); - pos_id_from.insert(h,pos_id); - pos_id - }) as u32 - }).collect(); - let map_tex_id:Vec=model.unique_tex.iter().map(|tex|{ - let h=tex.map(|v|bytemuck::cast::(v)); - (if let Some(&tex_id)=tex_id_from.get(&h){ - tex_id - }else{ - let tex_id=unique_tex.len(); - unique_tex.push(tex.clone()); - tex_id_from.insert(h,tex_id); - tex_id - }) as u32 - }).collect(); - let map_normal_id:Vec=model.unique_normal.iter().map(|untransformed_normal|{ - let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array(); - let h=normal.map(|v|bytemuck::cast::(v)); - (if let Some(&normal_id)=normal_id_from.get(&h){ - normal_id - }else{ - let normal_id=unique_normal.len(); - unique_normal.push(normal.clone()); - normal_id_from.insert(h,normal_id); - normal_id - }) as u32 - }).collect(); - let map_color_id:Vec=model.unique_color.iter().map(|color|{ - let h=color.map(|v|bytemuck::cast::(v)); - (if let Some(&color_id)=color_id_from.get(&h){ - color_id - }else{ - let color_id=unique_color.len(); - unique_color.push(color.clone()); - color_id_from.insert(h,color_id); - color_id - }) as u32 - }).collect(); - //map the indexed vertices onto new indices - //creating the vertex map is slightly different because the vertices are directly hashable - let map_vertex_id:Vec=model.unique_vertices.iter().map(|unmapped_vertex|{ - let vertex=model::IndexedVertex{ - pos:map_pos_id[unmapped_vertex.pos as usize] as u32, - tex:map_tex_id[unmapped_vertex.tex as usize] as u32, - normal:map_normal_id[unmapped_vertex.normal as usize] as u32, - color:map_color_id[unmapped_vertex.color as usize] as u32, - }; - (if let Some(&vertex_id)=vertex_id_from.get(&vertex){ - vertex_id - }else{ - let vertex_id=unique_vertices.len(); - unique_vertices.push(vertex.clone()); - vertex_id_from.insert(vertex,vertex_id); - vertex_id - }) as u32 - }).collect(); - for group in &model.groups{ - for poly in &group.polys{ - polys.push(model::IndexedPolygon{vertices:poly.vertices.iter().map(|&vertex_id|map_vertex_id[vertex_id as usize]).collect()}); - } - } - } - //push model into dedup - deduplicated_models.push(model_graphics::IndexedModelGraphicsSingleTexture{ - unique_pos, - unique_tex, - unique_normal, - unique_color, - unique_vertices, - texture, - groups:vec![model_graphics::IndexedGroupFixedTexture{ - polys - }], - instances:vec![model_graphics::ModelGraphicsInstance{ - transform:glam::Mat4::IDENTITY, - normal_transform:glam::Mat3::IDENTITY, - color - }], - }); - } - } - } - //fill untouched models - for (model_id,model) in unique_texture_models.into_iter().enumerate(){ - if !selected_model_instances.contains(&model_id){ - deduplicated_models.push(model); - } - } - - //de-index models - let deduplicated_models_len=deduplicated_models.len(); - let models:Vec=deduplicated_models.into_iter().map(|model|{ - let mut vertices = Vec::new(); - let mut index_from_vertex = std::collections::HashMap::new();//:: - let mut entities = Vec::new(); - //this mut be combined in a more complex way if the models use different render patterns per group - let mut indices = Vec::new(); - for group in model.groups { - for poly in group.polys { - for end_index in 2..poly.vertices.len() { - for &index in &[0, end_index - 1, end_index] { - let vertex_index = poly.vertices[index]; - if let Some(&i)=index_from_vertex.get(&vertex_index){ - indices.push(i); - }else{ - let i=vertices.len() as u16; - let vertex=&model.unique_vertices[vertex_index as usize]; - vertices.push(model_graphics::GraphicsVertex{ - pos: model.unique_pos[vertex.pos as usize], - tex: model.unique_tex[vertex.tex as usize], - normal: model.unique_normal[vertex.normal as usize], - color:model.unique_color[vertex.color as usize], - }); - index_from_vertex.insert(vertex_index,i); - indices.push(i); - } - } - } - } - } - entities.push(indices); - model_graphics::ModelGraphicsSingleTexture{ - instances:model.instances, - vertices, - entities, - texture:model.texture, - } - }).collect(); - //.into_iter() the modeldata vec so entities can be /moved/ to models.entities - let mut model_count=0; - let mut instance_count=0; - let uniform_buffer_binding_size=::required_limits().max_uniform_buffer_binding_size as usize; - let chunk_size=uniform_buffer_binding_size/MODEL_BUFFER_SIZE_BYTES; - self.graphics.models.reserve(models.len()); - for model in models.into_iter() { - instance_count+=model.instances.len(); - for instances_chunk in model.instances.rchunks(chunk_size){ - model_count+=1; - let model_uniforms = get_instances_buffer_data(instances_chunk); - let model_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(format!("Model{} Buf",model_count).as_str()), - contents: bytemuck::cast_slice(&model_uniforms), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - let texture_view=match model.texture{ - Some(texture_id)=>{ - match double_map.get(&texture_id){ - Some(&mapped_texture_id)=>&texture_views[mapped_texture_id as usize], - None=>&self.graphics.temp_squid_texture_view, - } - }, - None=>&self.graphics.temp_squid_texture_view, - }; - let model_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &self.graphics.bind_group_layouts.model, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: model_buf.as_entire_binding(), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(texture_view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(&self.graphics.samplers.repeat), - }, - ], - label: Some(format!("Model{} Bind Group",model_count).as_str()), - }); - let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex"), - contents: bytemuck::cast_slice(&model.vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - //all of these are being moved here - self.graphics.models.push(ModelGraphics{ - instances:instances_chunk.to_vec(), - vertex_buf, - entities: model.entities.iter().map(|indices|{ - let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index"), - contents: bytemuck::cast_slice(&indices), - usage: wgpu::BufferUsages::INDEX, - }); - Entity { - index_buf, - index_count: indices.len() as u32, - } - }).collect(), - bind_group: model_bind_group, - model_buf, - }); - } - } - println!("Texture References={}",num_textures); - println!("Textures Loaded={}",texture_views.len()); - println!("Indexed Models={}",indexed_models_len); - println!("Deduplicated Models={}",deduplicated_models_len); - println!("Graphics Objects: {}",self.graphics.models.len()); - println!("Graphics Instances: {}",instance_count); - } -} - -const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::(); -const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4; -fn get_instances_buffer_data(instances:&[ModelGraphicsInstance]) -> Vec { - let mut raw = Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len()); - for (i,mi) in instances.iter().enumerate(){ - let mut v = raw.split_off(MODEL_BUFFER_SIZE*i); - //model transform - raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]); - //normal transform - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis)); - raw.extend_from_slice(&[0.0]); - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis)); - raw.extend_from_slice(&[0.0]); - raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis)); - raw.extend_from_slice(&[0.0]); - //color - raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get())); - raw.append(&mut v); - } - raw -} - -impl framework::Example for GlobalState { - fn optional_features() -> wgpu::Features { - wgpu::Features::TEXTURE_COMPRESSION_ASTC - | wgpu::Features::TEXTURE_COMPRESSION_ETC2 - } - fn required_features() -> wgpu::Features { - wgpu::Features::TEXTURE_COMPRESSION_BC - } - fn required_limits() -> wgpu::Limits { - wgpu::Limits::default() //framework.rs was using goofy limits that caused me a multi-day headache - } - fn init( - config: &wgpu::SurfaceConfiguration, - _adapter: &wgpu::Adapter, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Self { - //wee - let user_settings=settings::read_user_settings(); - let mut indexed_models = Vec::new(); - indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),glam::Vec4::ONE)); - indexed_models.push(primitives::unit_sphere()); - indexed_models.push(primitives::unit_cylinder()); - indexed_models.push(primitives::unit_cube()); - println!("models.len = {:?}", indexed_models.len()); - indexed_models[0].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.))).unwrap(), - ..Default::default() - }); - //quad monkeys - indexed_models[1].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,10.))).unwrap(), - ..Default::default() - }); - indexed_models[1].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,10.))).unwrap(), - color:glam::vec4(1.0,0.0,0.0,1.0), - ..Default::default() - }); - indexed_models[1].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,20.))).unwrap(), - color:glam::vec4(0.0,1.0,0.0,1.0), - ..Default::default() - }); - indexed_models[1].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,20.))).unwrap(), - color:glam::vec4(0.0,0.0,1.0,1.0), - ..Default::default() - }); - //decorative monkey - indexed_models[1].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(15.,10.,15.))).unwrap(), - color:glam::vec4(0.5,0.5,0.5,0.5), - attributes:model::CollisionAttributes::Decoration, - ..Default::default() - }); - //teapot - indexed_models[2].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.))).unwrap(), - ..Default::default() - }); - //ground - indexed_models[3].instances.push(model::ModelInstance{ - transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0))).unwrap(), - ..Default::default() - }); - - let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], - }); - let skybox_texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Skybox Texture Bind Group Layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - multisampled: false, - view_dimension: wgpu::TextureViewDimension::Cube, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let model_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Model Bind Group Layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - - let clamp_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label: Some("Clamp 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::Linear, - ..Default::default() - }); - let repeat_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label: Some("Repeat Sampler"), - address_mode_u: wgpu::AddressMode::Repeat, - address_mode_v: wgpu::AddressMode::Repeat, - address_mode_w: wgpu::AddressMode::Repeat, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); - - // Create the render pipeline - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); - - //load textures - let device_features = device.features(); - - let skybox_texture_view={ - let skybox_format = if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ASTC) { - log::info!("Using ASTC"); - wgpu::TextureFormat::Astc { - block: AstcBlock::B4x4, - channel: AstcChannel::UnormSrgb, - } - } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_ETC2) { - log::info!("Using ETC2"); - wgpu::TextureFormat::Etc2Rgb8UnormSrgb - } else if device_features.contains(wgpu::Features::TEXTURE_COMPRESSION_BC) { - log::info!("Using BC"); - wgpu::TextureFormat::Bc1RgbaUnormSrgb - } else { - log::info!("Using plain"); - wgpu::TextureFormat::Bgra8UnormSrgb - }; - - let bytes = match skybox_format { - wgpu::TextureFormat::Astc { - block: AstcBlock::B4x4, - channel: AstcChannel::UnormSrgb, - } => &include_bytes!("../images/astc.dds")[..], - wgpu::TextureFormat::Etc2Rgb8UnormSrgb => &include_bytes!("../images/etc2.dds")[..], - wgpu::TextureFormat::Bc1RgbaUnormSrgb => &include_bytes!("../images/bc1.dds")[..], - wgpu::TextureFormat::Bgra8UnormSrgb => &include_bytes!("../images/bgra.dds")[..], - _ => unreachable!(), - }; - - let skybox_image = ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap(); - - let size = wgpu::Extent3d { - width: skybox_image.get_width(), - height: skybox_image.get_height(), - depth_or_array_layers: 6, - }; - - let layer_size = wgpu::Extent3d { - depth_or_array_layers: 1, - ..size - }; - let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); - - log::debug!( - "Copying {:?} skybox images of size {}, {}, 6 with {} mips to gpu", - skybox_format, - size.width, - size.height, - max_mips, - ); - - let skybox_texture = device.create_texture_with_data( - queue, - &wgpu::TextureDescriptor { - size, - mip_level_count: max_mips, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: skybox_format, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - label: Some("Skybox Texture"), - view_formats: &[], - }, - &skybox_image.data, - ); - - skybox_texture.create_view(&wgpu::TextureViewDescriptor { - label: Some("Skybox Texture View"), - dimension: Some(wgpu::TextureViewDimension::Cube), - ..wgpu::TextureViewDescriptor::default() - }) - }; - - //squid - let squid_texture_view={ - let bytes = include_bytes!("../images/squid.dds"); - - let image = ddsfile::Dds::read(&mut std::io::Cursor::new(bytes)).unwrap(); - - let size = wgpu::Extent3d { - width: image.get_width(), - height: image.get_height(), - depth_or_array_layers: 1, - }; - - let layer_size = wgpu::Extent3d { - depth_or_array_layers: 1, - ..size - }; - let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); - - let texture = device.create_texture_with_data( - queue, - &wgpu::TextureDescriptor { - size, - mip_level_count: max_mips, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Bc7RgbaUnorm, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - label: Some("Squid Texture"), - view_formats: &[], - }, - &image.data, - ); - - texture.create_view(&wgpu::TextureViewDescriptor { - label: Some("Squid Texture View"), - dimension: Some(wgpu::TextureViewDimension::D2), - ..wgpu::TextureViewDescriptor::default() - }) - }; - - let model_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[ - &camera_bind_group_layout, - &skybox_texture_bind_group_layout, - &model_bind_group_layout, - ], - push_constant_ranges: &[], - }); - let sky_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[ - &camera_bind_group_layout, - &skybox_texture_bind_group_layout, - ], - push_constant_ranges: &[], - }); - - // Create the render pipelines - let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Sky Pipeline"), - layout: Some(&sky_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_sky", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_sky", - targets: &[Some(config.view_formats[0].into())], - }), - primitive: wgpu::PrimitiveState { - front_face: wgpu::FrontFace::Cw, - ..Default::default() - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Self::DEPTH_FORMAT, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::LessEqual, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - let model_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Model Pipeline"), - layout: Some(&model_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_entity_texture", - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2, 2 => Float32x3, 3 => Float32x4], - }], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_entity_texture", - targets: &[Some(config.view_formats[0].into())], - }), - primitive: wgpu::PrimitiveState { - front_face: wgpu::FrontFace::Cw, - ..Default::default() - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: Self::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - let mut physics = physics::PhysicsState::default(); - - physics.load_user_settings(&user_settings); - - let screen_size=glam::uvec2(config.width,config.height); - - let camera=GraphicsCamera::new(screen_size,user_settings.calculate_fov(1.0,&screen_size).as_vec2()); - let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&physics::MouseState::default())); - let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Camera"), - contents: bytemuck::cast_slice(&camera_uniforms), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &camera_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: camera_buf.as_entire_binding(), - }, - ], - label: Some("Camera"), - }); - - let skybox_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &skybox_texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&skybox_texture_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&clamp_sampler), - }, - ], - label: Some("Sky Texture"), - }); - - let depth_view = Self::create_depth_texture(config, device); - - let mut graphics=GraphicsState { - pipelines:GraphicsPipelines{ - skybox:sky_pipeline, - model:model_pipeline - }, - bind_groups:GraphicsBindGroups{ - camera:camera_bind_group, - skybox_texture:skybox_texture_bind_group, - }, - camera, - camera_buf, - models: Vec::new(), - depth_view, - staging_belt: wgpu::util::StagingBelt::new(0x100), - bind_group_layouts: GraphicsBindGroupLayouts { model: model_bind_group_layout }, - samplers: GraphicsSamplers { repeat: repeat_sampler }, - temp_squid_texture_view: squid_texture_view, - }; - - graphics.load_user_settings(&user_settings); - - let indexed_model_instances=model::IndexedModelInstances{ - textures:Vec::new(), - models:indexed_models, - spawn_point:integer::Planar64Vec3::Y*50, - modes:Vec::new(), - }; - - //how to multithread - - //1. build - physics.generate_models(&indexed_model_instances); - - //2. move - let physics_thread=physics.into_worker(); - - //3. forget - - let mut state=GlobalState{ - start_time:Instant::now(), - manual_mouse_lock:false, - mouse:physics::MouseState::default(), - user_settings, - graphics, - physics_thread, - }; - state.generate_model_graphics(&device,&queue,indexed_model_instances); - - let args:Vec=std::env::args().collect(); - if args.len()==2{ - state.load_file(std::path::PathBuf::from(&args[1]), device, queue); - } - - return state; - } - - fn load_file(&mut self,path: std::path::PathBuf, device: &wgpu::Device, queue: &wgpu::Queue){ - println!("Loading file: {:?}", &path); - //oh boy! let's load the map! - if let Ok(file)=std::fs::File::open(path){ - let mut input = std::io::BufReader::new(file); - let mut first_8=[0u8;8]; - //.rbxm roblox binary = "{ - match match &first_8[4..8]{ - b"lox!"=>rbx_binary::from_reader(input).map_err(|e|format!("{:?}",e)), - b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(|e|format!("{:?}",e)), - other=>Err(format!("Unknown Roblox file type {:?}",other)), - }{ - Ok(dom)=>Some(load_roblox::generate_indexed_models(dom)), - Err(e)=>{ - println!("Error loading roblox file:{:?}",e); - None - }, - } - }, - //b"VBSP"=>Some(load_bsp::generate_indexed_models(input)), - //b"SNFM"=>Some(sniffer::generate_indexed_models(input)), - //b"SNFB"=>Some(sniffer::load_bot(input)), - other=>{ - println!("loser file {:?}",other); +mod compat_worker; +mod model_graphics; +mod physics_worker; +mod graphics_worker; + +fn load_file(path: std::path::PathBuf)->Option{ + println!("Loading file: {:?}", &path); + //oh boy! let's load the map! + if let Ok(file)=std::fs::File::open(path){ + let mut input = std::io::BufReader::new(file); + let mut first_8=[0u8;8]; + //.rbxm roblox binary = "{ + match match &first_8[4..8]{ + b"lox!"=>rbx_binary::from_reader(input).map_err(|e|format!("{:?}",e)), + b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(|e|format!("{:?}",e)), + other=>Err(format!("Unknown Roblox file type {:?}",other)), + }{ + Ok(dom)=>Some(load_roblox::generate_indexed_models(dom)), + Err(e)=>{ + println!("Error loading roblox file:{:?}",e); None }, } - }{ - let spawn_point=indexed_model_instances.spawn_point; - //if generate_indexed_models succeeds, clear the previous ones - self.graphics.clear(); - - let mut physics=physics::PhysicsState::default(); - //physics.spawn() - physics.game.stage_id=0; - physics.spawn_point=spawn_point; - physics.process_instruction(instruction::TimedInstruction{ - time:physics.time, - instruction: PhysicsInstruction::Input(physics::PhysicsInputInstruction::Reset), - }); - physics.load_user_settings(&self.user_settings); - physics.generate_models(&indexed_model_instances); - self.physics_thread=physics.into_worker(); - - //graphics.load_user_settings(&self.user_settings); - self.generate_model_graphics(device,queue,indexed_model_instances); - //manual reset - }else{ - println!("No modeldatas were generated"); - } - }else{ - println!("Failed to read first 8 bytes and seek back to beginning of file."); + }, + //b"VBSP"=>Some(load_bsp::generate_indexed_models(input)), + //b"SNFM"=>Some(sniffer::generate_indexed_models(input)), + //b"SNFB"=>Some(sniffer::load_bot(input)), + other=>{ + println!("loser file {:?}",other); + None + }, } }else{ - println!("Could not open file"); + println!("Failed to read first 8 bytes and seek back to beginning of file."); + None } - } - - #[allow(clippy::single_match)] - fn update(&mut self, window: &winit::window::Window, device: &wgpu::Device, queue: &wgpu::Queue, event: winit::event::WindowEvent) { - let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64); - match event { - winit::event::WindowEvent::DroppedFile(path) => self.load_file(path,device,queue), - winit::event::WindowEvent::Focused(state)=>{ - //pause unpause - //recalculate pressed keys on focus - }, - winit::event::WindowEvent::KeyboardInput { - input:winit::event::KeyboardInput{state, virtual_keycode,..}, - .. - }=>{ - let s=match state { - winit::event::ElementState::Pressed => true, - winit::event::ElementState::Released => false, - }; - match virtual_keycode{ - Some(winit::event::VirtualKeyCode::Tab)=>{ - if s{ - self.manual_mouse_lock=false; - match window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.graphics.camera.screen_size.x as f32/2.0, self.graphics.camera.screen_size.y as f32/2.0)){ - Ok(())=>(), - Err(e)=>println!("Could not set cursor position: {:?}",e), - } - match window.set_cursor_grab(winit::window::CursorGrabMode::None){ - Ok(())=>(), - Err(e)=>println!("Could not release cursor: {:?}",e), - } - }else{ - //if cursor is outside window don't lock but apparently there's no get pos function - //let pos=window.get_cursor_pos(); - match window.set_cursor_grab(winit::window::CursorGrabMode::Locked){ - Ok(())=>(), - Err(_)=>{ - match window.set_cursor_grab(winit::window::CursorGrabMode::Confined){ - Ok(())=>(), - Err(e)=>{ - self.manual_mouse_lock=true; - println!("Could not confine cursor: {:?}",e) - }, - } - } - } - } - window.set_cursor_visible(s); - }, - Some(winit::event::VirtualKeyCode::F11)=>{ - if s{ - if window.fullscreen().is_some(){ - window.set_fullscreen(None); - }else{ - window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); - } - } - }, - Some(winit::event::VirtualKeyCode::Escape)=>{ - if s{ - self.manual_mouse_lock=false; - match window.set_cursor_grab(winit::window::CursorGrabMode::None){ - Ok(())=>(), - Err(e)=>println!("Could not release cursor: {:?}",e), - } - window.set_cursor_visible(true); - } - }, - Some(keycode)=>{ - if let Some(input_instruction)=match keycode { - winit::event::VirtualKeyCode::W => Some(InputInstruction::MoveForward(s)), - winit::event::VirtualKeyCode::A => Some(InputInstruction::MoveLeft(s)), - winit::event::VirtualKeyCode::S => Some(InputInstruction::MoveBack(s)), - winit::event::VirtualKeyCode::D => Some(InputInstruction::MoveRight(s)), - winit::event::VirtualKeyCode::E => Some(InputInstruction::MoveUp(s)), - winit::event::VirtualKeyCode::Q => Some(InputInstruction::MoveDown(s)), - winit::event::VirtualKeyCode::Space => Some(InputInstruction::Jump(s)), - winit::event::VirtualKeyCode::Z => Some(InputInstruction::Zoom(s)), - winit::event::VirtualKeyCode::R => if s{Some(InputInstruction::Reset)}else{None}, - _ => None, - }{ - self.physics_thread.send(TimedInstruction{ - time, - instruction:input_instruction, - }).unwrap(); - } - }, - _=>(), - } - }, - _=>(), - } - } - - fn device_event(&mut self, window: &winit::window::Window, event: winit::event::DeviceEvent) { - //there's no way this is the best way get a timestamp. - let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64); - match event { - winit::event::DeviceEvent::MouseMotion { - delta,//these (f64,f64) are integers on my machine - } => { - if self.manual_mouse_lock{ - match window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.graphics.camera.screen_size.x as f32/2.0, self.graphics.camera.screen_size.y as f32/2.0)){ - Ok(())=>(), - Err(e)=>println!("Could not set cursor position: {:?}",e), - } - } - //do not step the physics because the mouse polling rate is higher than the physics can run. - //essentially the previous input will be overwritten until a true step runs - //which is fine because they run all the time. - let delta=glam::ivec2(delta.0 as i32,delta.1 as i32); - self.mouse.pos+=delta; - self.physics_thread.send(TimedInstruction{ - time, - instruction:InputInstruction::MoveMouse(self.mouse.pos), - }).unwrap(); - }, - winit::event::DeviceEvent::MouseWheel { - delta, - } => { - println!("mousewheel {:?}",delta); - if false{//self.physics.style.use_scroll{ - self.physics_thread.send(TimedInstruction{ - time, - instruction:InputInstruction::Jump(true),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump - }).unwrap(); - } - } - _=>(), - } - } - - fn resize( - &mut self, - config: &wgpu::SurfaceConfiguration, - device: &wgpu::Device, - _queue: &wgpu::Queue, - ) { - self.graphics.depth_view = Self::create_depth_texture(config, device); - self.graphics.camera.screen_size=glam::uvec2(config.width, config.height); - self.graphics.load_user_settings(&self.user_settings); - } - - fn render( - &mut self, - view: &wgpu::TextureView, - device: &wgpu::Device, - queue: &wgpu::Queue, - _spawner: &framework::Spawner, - ) { - //ideally this would be scheduled to execute and finish right before the render. - let time=integer::Time::from_nanos(self.start_time.elapsed().as_nanos() as i64); - self.physics_thread.send(TimedInstruction{ - time, - instruction:InputInstruction::Idle, - }).unwrap(); - //update time lol - self.mouse.time=time; - - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - - // update rotation - let camera_uniforms = self.graphics.camera.to_uniform_data(self.physics_thread.grab_clone().adjust_mouse(&self.mouse)); - self.graphics.staging_belt - .write_buffer( - &mut encoder, - &self.graphics.camera_buf, - 0, - wgpu::BufferSize::new((camera_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), - device, - ) - .copy_from_slice(bytemuck::cast_slice(&camera_uniforms)); - //This code only needs to run when the uniforms change - /* - for model in self.graphics.models.iter() { - let model_uniforms = get_instances_buffer_data(&model.instances); - self.graphics.staging_belt - .write_buffer( - &mut encoder, - &model.model_buf,//description of where data will be written when command is executed - 0,//offset in staging belt? - wgpu::BufferSize::new((model_uniforms.len() * 4) as wgpu::BufferAddress).unwrap(), - device, - ) - .copy_from_slice(bytemuck::cast_slice(&model_uniforms)); - } - */ - self.graphics.staging_belt.finish(); - - { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, - }, - })], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { - view: &self.graphics.depth_view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: false, - }), - stencil_ops: None, - }), - }); - - rpass.set_bind_group(0, &self.graphics.bind_groups.camera, &[]); - rpass.set_bind_group(1, &self.graphics.bind_groups.skybox_texture, &[]); - - rpass.set_pipeline(&self.graphics.pipelines.model); - for model in self.graphics.models.iter() { - rpass.set_bind_group(2, &model.bind_group, &[]); - rpass.set_vertex_buffer(0, model.vertex_buf.slice(..)); - - for entity in model.entities.iter() { - rpass.set_index_buffer(entity.index_buf.slice(..), wgpu::IndexFormat::Uint16); - rpass.draw_indexed(0..entity.index_count, 0, 0..model.instances.len() as u32); - } - } - - rpass.set_pipeline(&self.graphics.pipelines.skybox); - rpass.draw(0..3, 0..1); - } - - queue.submit(std::iter::once(encoder.finish())); - - self.graphics.staging_belt.recall(); + }else{ + println!("Could not open file"); + None } } -fn main() { - framework::run::( - format!("Strafe Client v{}", - env!("CARGO_PKG_VERSION") - ).as_str() - ); +pub fn default_models()->model::IndexedModelInstances{ + let mut indexed_models = Vec::new(); + indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),glam::Vec4::ONE)); + indexed_models.push(primitives::unit_sphere()); + indexed_models.push(primitives::unit_cylinder()); + indexed_models.push(primitives::unit_cube()); + println!("models.len = {:?}", indexed_models.len()); + indexed_models[0].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.))).unwrap(), + ..Default::default() + }); + //quad monkeys + indexed_models[1].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,10.))).unwrap(), + ..Default::default() + }); + indexed_models[1].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,10.))).unwrap(), + color:glam::vec4(1.0,0.0,0.0,1.0), + ..Default::default() + }); + indexed_models[1].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(10.,5.,20.))).unwrap(), + color:glam::vec4(0.0,1.0,0.0,1.0), + ..Default::default() + }); + indexed_models[1].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(20.,5.,20.))).unwrap(), + color:glam::vec4(0.0,0.0,1.0,1.0), + ..Default::default() + }); + //decorative monkey + indexed_models[1].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(15.,10.,15.))).unwrap(), + color:glam::vec4(0.5,0.5,0.5,0.5), + attributes:model::CollisionAttributes::Decoration, + ..Default::default() + }); + //teapot + indexed_models[2].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.))).unwrap(), + ..Default::default() + }); + //ground + indexed_models[3].instances.push(model::ModelInstance{ + transform:integer::Planar64Affine3::try_from(glam::Affine3A::from_translation(glam::vec3(0.,0.,0.))*glam::Affine3A::from_scale(glam::vec3(160.0, 1.0, 160.0))).unwrap(), + ..Default::default() + }); + model::IndexedModelInstances{ + textures:Vec::new(), + models:indexed_models, + spawn_point:integer::Planar64Vec3::Y*50, + modes:Vec::new(), + } +} + +fn main(){ + let context=setup::setup(format!("Strafe Client v{}",env!("CARGO_PKG_VERSION")).as_str()); + context.start();//creates and runs a window context } diff --git a/src/physics.rs b/src/physics.rs index 347d280..9080525 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -30,26 +30,13 @@ pub enum PhysicsInputInstruction { SetJump(bool), SetZoom(bool), Reset, - Idle, -} -#[derive(Debug)] -pub enum InputInstruction { - MoveMouse(glam::IVec2), - MoveRight(bool), - MoveUp(bool), - MoveBack(bool), - MoveLeft(bool), - MoveDown(bool), - MoveForward(bool), - Jump(bool), - Zoom(bool), - Reset, Idle, //Idle: there were no input events, but the simulation is safe to advance to this timestep //for interpolation / networking / playback reasons, most playback heads will always want //to be 1 instruction ahead to generate the next state for interpolation. } -#[derive(Clone,Hash)] + +#[derive(Clone,Hash,Default)] pub struct Body { position: Planar64Vec3,//I64 where 2^32 = 1 u velocity: Planar64Vec3,//I64 where 2^32 = 1 u/s @@ -257,6 +244,19 @@ impl PhysicsCamera { } } +impl std::default::Default for PhysicsCamera{ + fn default()->Self{ + Self{ + offset:Planar64Vec3::ZERO,//TODO: delete this from PhysicsCamera, it should be GraphicsCamera only + sensitivity:Ratio64Vec2::ONE*200_000, + mouse:MouseState::default(),//t=0 does not cause divide by zero because it's immediately replaced + clamped_mouse_pos:glam::IVec2::ZERO, + angle_pitch_lower_limit:-Angle32::FRAC_PI_2, + angle_pitch_upper_limit:Angle32::FRAC_PI_2, + } + } +} + pub struct GameMechanicsState{ pub stage_id:u32, //jump_counts:HashMap, @@ -523,15 +523,15 @@ enum MoveState{ } pub struct PhysicsState{ - pub time:Time, + time:Time, body:Body, world:WorldState,//currently there is only one state the world can be in - pub game:GameMechanicsState, + game:GameMechanicsState, style:StyleModifiers, touching:TouchingState, //camera must exist in state because wormholes modify the camera, also camera punch camera:PhysicsCamera, - next_mouse:MouseState,//Where is the mouse headed next + pub next_mouse:MouseState,//Where is the mouse headed next controls:u32, move_state:MoveState, //all models @@ -541,16 +541,16 @@ pub struct PhysicsState{ modes:Modes, //the spawn point is where you spawn when you load into the map. //This is not the same as Reset which teleports you to Spawn0 - pub spawn_point:Planar64Vec3, + spawn_point:Planar64Vec3, } -#[derive(Clone)] +#[derive(Clone,Default)] pub struct PhysicsOutputState{ camera:PhysicsCamera, body:Body, } impl PhysicsOutputState{ - pub fn adjust_mouse(&self,mouse:&MouseState)->(glam::Vec3,glam::Vec2){ - ((self.body.extrapolated_position(mouse.time)+self.camera.offset).into(),self.camera.simulate_move_angles(mouse.pos)) + pub fn extrapolate(&self,mouse_pos:glam::IVec2,time:Time)->(glam::Vec3,glam::Vec2){ + ((self.body.extrapolated_position(time)+self.camera.offset).into(),self.camera.simulate_move_angles(mouse_pos)) } } @@ -734,93 +734,7 @@ impl PhysicsState { self.models.clear(); self.modes.clear(); self.touching.clear(); - } - - pub fn into_worker(mut self)->crate::worker::CompatWorker,PhysicsOutputState,Box)->PhysicsOutputState>>{ - let mut mouse_blocking=true; - let mut last_mouse_time=self.next_mouse.time; - let mut timeline=std::collections::VecDeque::new(); - crate::worker::CompatWorker::new(self.output(),Box::new(move |ins:TimedInstruction|{ - if if let Some(phys_input)=match ins.instruction{ - InputInstruction::MoveMouse(m)=>{ - if mouse_blocking{ - //tell the game state which is living in the past about its future - timeline.push_front(TimedInstruction{ - time:last_mouse_time, - instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), - }); - }else{ - //mouse has just started moving again after being still for longer than 10ms. - //replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero - timeline.push_front(TimedInstruction{ - time:last_mouse_time, - instruction:PhysicsInputInstruction::ReplaceMouse( - MouseState{time:last_mouse_time,pos:self.next_mouse.pos}, - MouseState{time:ins.time,pos:m} - ), - }); - //delay physics execution until we have an interpolation target - mouse_blocking=true; - } - last_mouse_time=ins.time; - None - }, - InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), - InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), - InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), - InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), - InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), - InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), - InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), - InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), - InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), - InputInstruction::Idle=>Some(PhysicsInputInstruction::Idle), - }{ - //non-mouse event - timeline.push_back(TimedInstruction{ - time:ins.time, - instruction:phys_input, - }); - - if mouse_blocking{ - //assume the mouse has stopped moving after 10ms. - //shitty mice are 125Hz which is 8ms so this should cover that. - //setting this to 100us still doesn't print even though it's 10x lower than the polling rate, - //so mouse events are probably not handled separately from drawing and fire right before it :( - if Time::from_millis(10)PhysicsOutputState{ @@ -830,6 +744,15 @@ impl PhysicsState { } } + pub fn spawn(&mut self,spawn_point:Planar64Vec3){ + self.game.stage_id=0; + self.spawn_point=spawn_point; + self.process_instruction(crate::instruction::TimedInstruction{ + time:self.time, + instruction: PhysicsInstruction::Input(PhysicsInputInstruction::Reset), + }); + } + pub fn generate_models(&mut self,indexed_models:&crate::model::IndexedModelInstances){ let mut starts=Vec::new(); let mut spawns=Vec::new(); @@ -1357,12 +1280,12 @@ impl crate::instruction::InstructionConsumer for PhysicsStat } //selectively update body match &ins.instruction { - //PhysicsInstruction::Input(InputInstruction::MoveMouse(_)) => (),//dodge time for mouse movement + PhysicsInstruction::Input(PhysicsInputInstruction::Idle)=>self.time=ins.time,//idle simply updates time PhysicsInstruction::Input(_) |PhysicsInstruction::ReachWalkTargetVelocity |PhysicsInstruction::CollisionStart(_) |PhysicsInstruction::CollisionEnd(_) - |PhysicsInstruction::StrafeTick => self.advance_time(ins.time), + |PhysicsInstruction::StrafeTick=>self.advance_time(ins.time), } match ins.instruction { PhysicsInstruction::CollisionStart(c) => { diff --git a/src/physics_worker.rs b/src/physics_worker.rs new file mode 100644 index 0000000..093d807 --- /dev/null +++ b/src/physics_worker.rs @@ -0,0 +1,133 @@ +use crate::integer::Time; +use crate::physics::{MouseState,PhysicsInputInstruction}; +use crate::instruction::{TimedInstruction,InstructionConsumer}; +#[derive(Debug)] +pub enum InputInstruction { + MoveMouse(glam::IVec2), + MoveRight(bool), + MoveUp(bool), + MoveBack(bool), + MoveLeft(bool), + MoveDown(bool), + MoveForward(bool), + Jump(bool), + Zoom(bool), + Reset, +} +pub enum Instruction{ + Input(InputInstruction), + Render, + Resize(winit::dpi::PhysicalSize,crate::settings::UserSettings), + GenerateModels(crate::model::IndexedModelInstances), + ClearModels, + //Graphics(crate::graphics_worker::Instruction), +} + + pub fn new(mut physics:crate::physics::PhysicsState,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ + let mut mouse_blocking=true; + let mut last_mouse_time=physics.next_mouse.time; + let mut timeline=std::collections::VecDeque::new(); + crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ + if if let Some(phys_input)=match &ins.instruction{ + Instruction::Input(input_instruction)=>match input_instruction{ + &InputInstruction::MoveMouse(m)=>{ + if mouse_blocking{ + //tell the game state which is living in the past about its future + timeline.push_front(TimedInstruction{ + time:last_mouse_time, + instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), + }); + }else{ + //mouse has just started moving again after being still for longer than 10ms. + //replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero + timeline.push_front(TimedInstruction{ + time:last_mouse_time, + instruction:PhysicsInputInstruction::ReplaceMouse( + MouseState{time:last_mouse_time,pos:physics.next_mouse.pos}, + MouseState{time:ins.time,pos:m} + ), + }); + //delay physics execution until we have an interpolation target + mouse_blocking=true; + } + last_mouse_time=ins.time; + None + }, + &InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), + &InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), + &InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), + &InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), + &InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), + &InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), + &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), + &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), + InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), + }, + Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), + Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), + Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle), + Instruction::Render=>Some(PhysicsInputInstruction::Idle), + }{ + //non-mouse event + timeline.push_back(TimedInstruction{ + time:ins.time, + instruction:phys_input, + }); + + if mouse_blocking{ + //assume the mouse has stopped moving after 10ms. + //shitty mice are 125Hz which is 8ms so this should cover that. + //setting this to 100us still doesn't print even though it's 10x lower than the polling rate, + //so mouse events are probably not handled separately from drawing and fire right before it :( + if Time::from_millis(10){ + graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.next_mouse.pos)).unwrap(); + }, + Instruction::Resize(size,user_settings)=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); + }, + Instruction::GenerateModels(indexed_model_instances)=>{ + physics.generate_models(&indexed_model_instances); + physics.spawn(indexed_model_instances.spawn_point); + graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(indexed_model_instances)).unwrap(); + }, + Instruction::ClearModels=>{ + physics.clear(); + graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap(); + }, + _=>(), + } + }) + } \ No newline at end of file diff --git a/src/settings.rs b/src/settings.rs index 24ac840..d8ad48c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,11 +1,14 @@ use crate::integer::{Ratio64,Ratio64Vec2}; +#[derive(Clone)] struct Ratio{ ratio:f64, } +#[derive(Clone)] enum DerivedFov{ FromScreenAspect, FromAspect(Ratio), } +#[derive(Clone)] enum Fov{ Exactly{x:f64,y:f64}, SpecifyXDeriveY{x:f64,y:DerivedFov}, @@ -16,9 +19,11 @@ impl Default for Fov{ Fov::SpecifyYDeriveX{x:DerivedFov::FromScreenAspect,y:1.0} } } +#[derive(Clone)] enum DerivedSensitivity{ FromRatio(Ratio64), } +#[derive(Clone)] enum Sensitivity{ Exactly{x:Ratio64,y:Ratio64}, SpecifyXDeriveY{x:Ratio64,y:DerivedSensitivity}, @@ -30,7 +35,7 @@ impl Default for Sensitivity{ } } -#[derive(Default)] +#[derive(Default,Clone)] pub struct UserSettings{ fov:Fov, sensitivity:Sensitivity, diff --git a/src/setup.rs b/src/setup.rs new file mode 100644 index 0000000..1c05ef4 --- /dev/null +++ b/src/setup.rs @@ -0,0 +1,300 @@ +use crate::instruction::TimedInstruction; +use crate::window::WindowInstruction; + +fn optional_features()->wgpu::Features{ + wgpu::Features::TEXTURE_COMPRESSION_ASTC + |wgpu::Features::TEXTURE_COMPRESSION_ETC2 +} +fn required_features()->wgpu::Features{ + wgpu::Features::TEXTURE_COMPRESSION_BC +} +fn required_downlevel_capabilities()->wgpu::DownlevelCapabilities{ + wgpu::DownlevelCapabilities{ + flags:wgpu::DownlevelFlags::empty(), + shader_model:wgpu::ShaderModel::Sm5, + ..wgpu::DownlevelCapabilities::default() + } +} +pub fn required_limits()->wgpu::Limits{ + wgpu::Limits::default() +} + +struct SetupContextPartial1{ + backends:wgpu::Backends, + instance:wgpu::Instance, +} +fn create_window(title:&str,event_loop:&winit::event_loop::EventLoop<()>)->Result{ + let mut builder = winit::window::WindowBuilder::new(); + builder = builder.with_title(title); + #[cfg(windows_OFF)] // TODO + { + use winit::platform::windows::WindowBuilderExtWindows; + builder = builder.with_no_redirection_bitmap(true); + } + builder.build(event_loop) +} +fn create_instance()->SetupContextPartial1{ + let backends=wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); + let dx12_shader_compiler=wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default(); + SetupContextPartial1{ + backends, + instance:wgpu::Instance::new(wgpu::InstanceDescriptor{ + backends, + dx12_shader_compiler, + }), + } +} +impl SetupContextPartial1{ + fn create_surface(self,window:&winit::window::Window)->Result{ + Ok(SetupContextPartial2{ + backends:self.backends, + surface:unsafe{self.instance.create_surface(window)}?, + instance:self.instance, + }) + } +} +struct SetupContextPartial2{ + backends:wgpu::Backends, + instance:wgpu::Instance, + surface:wgpu::Surface, +} +impl SetupContextPartial2{ + fn pick_adapter(self)->SetupContextPartial3{ + let adapter; + + let optional_features=optional_features(); + let required_features=required_features(); + + //no helper function smh gotta write it myself + let adapters=self.instance.enumerate_adapters(self.backends); + + let mut chosen_adapter=None; + let mut chosen_adapter_score=0; + for adapter in adapters { + if !adapter.is_surface_supported(&self.surface) { + continue; + } + + let score=match adapter.get_info().device_type{ + wgpu::DeviceType::IntegratedGpu=>3, + wgpu::DeviceType::DiscreteGpu=>4, + wgpu::DeviceType::VirtualGpu=>2, + wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1, + }; + + let adapter_features=adapter.features(); + if chosen_adapter_score= required_downlevel_capabilities.shader_model, + "Adapter does not support the minimum shader model required to run this example: {:?}", + required_downlevel_capabilities.shader_model + ); + assert!( + downlevel_capabilities + .flags + .contains(required_downlevel_capabilities.flags), + "Adapter does not support the downlevel capabilities required to run this example: {:?}", + required_downlevel_capabilities.flags - downlevel_capabilities.flags + ); + SetupContextPartial3{ + instance:self.instance, + surface:self.surface, + adapter, + } + } +} +struct SetupContextPartial3{ + instance:wgpu::Instance, + surface:wgpu::Surface, + adapter:wgpu::Adapter, +} +impl SetupContextPartial3{ + fn request_device(self)->SetupContextPartial4{ + let optional_features=optional_features(); + let required_features=required_features(); + + // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface. + let needed_limits=required_limits().using_resolution(self.adapter.limits()); + + let trace_dir=std::env::var("WGPU_TRACE"); + let (device, queue)=pollster::block_on(self.adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: (optional_features & self.adapter.features()) | required_features, + limits: needed_limits, + }, + trace_dir.ok().as_ref().map(std::path::Path::new), + )) + .expect("Unable to find a suitable GPU adapter!"); + + SetupContextPartial4{ + instance:self.instance, + surface:self.surface, + adapter:self.adapter, + device, + queue, + } + } +} +struct SetupContextPartial4{ + instance:wgpu::Instance, + surface:wgpu::Surface, + adapter:wgpu::Adapter, + device:wgpu::Device, + queue:wgpu::Queue, +} +impl SetupContextPartial4{ + fn configure_surface(self,size:&winit::dpi::PhysicalSize)->SetupContext{ + let mut config=self.surface + .get_default_config(&self.adapter, size.width, size.height) + .expect("Surface isn't supported by the adapter."); + let surface_view_format=config.format.add_srgb_suffix(); + config.view_formats.push(surface_view_format); + self.surface.configure(&self.device, &config); + + SetupContext{ + instance:self.instance, + surface:self.surface, + device:self.device, + queue:self.queue, + config, + } + } +} +pub struct SetupContext{ + pub instance:wgpu::Instance, + pub surface:wgpu::Surface, + pub device:wgpu::Device, + pub queue:wgpu::Queue, + pub config:wgpu::SurfaceConfiguration, +} + +pub fn setup(title:&str)->SetupContextSetup{ + let event_loop=winit::event_loop::EventLoop::new().unwrap(); + + let window=create_window(title,&event_loop).unwrap(); + + println!("Initializing the surface..."); + + let partial_1=create_instance(); + + let partial_2=partial_1.create_surface(&window).unwrap(); + + let partial_3=partial_2.pick_adapter(); + + let partial_4=partial_3.request_device(); + + SetupContextSetup{ + window, + event_loop, + partial_context:partial_4, + } +} + +pub struct SetupContextSetup{ + window:winit::window::Window, + event_loop:winit::event_loop::EventLoop<()>, + partial_context:SetupContextPartial4, +} + +impl SetupContextSetup{ + fn into_split(self)->(winit::window::Window,winit::event_loop::EventLoop<()>,SetupContext){ + let size=self.window.inner_size(); + //Steal values and drop self + ( + self.window, + self.event_loop, + self.partial_context.configure_surface(&size), + ) + } + pub fn start(self){ + let (window,event_loop,setup_context)=self.into_split(); + + //dedicated thread to ping request redraw back and resize the window doesn't seem logical + + let window=crate::window::WindowContextSetup::new(&setup_context,window); + //the thread that spawns the physics thread + let window_thread=window.into_worker(setup_context); + + println!("Entering event loop..."); + let root_time=std::time::Instant::now(); + run_event_loop(event_loop,window_thread,root_time).unwrap(); + } +} + +fn run_event_loop( + event_loop:winit::event_loop::EventLoop<()>, + mut window_thread:crate::compat_worker::QNWorker>, + root_time:std::time::Instant + )->Result<(),winit::error::EventLoopError>{ + event_loop.run(move |event,elwt|{ + let time=crate::integer::Time::from_nanos(root_time.elapsed().as_nanos() as i64); + // *control_flow=if cfg!(feature="metal-auto-capture"){ + // winit::event_loop::ControlFlow::Exit + // }else{ + // winit::event_loop::ControlFlow::Poll + // }; + match event{ + winit::event::Event::AboutToWait=>{ + window_thread.send(TimedInstruction{time,instruction:WindowInstruction::RequestRedraw}).unwrap(); + } + winit::event::Event::WindowEvent { + event: + // WindowEvent::Resized(size) + // | WindowEvent::ScaleFactorChanged { + // new_inner_size: &mut size, + // .. + // }, + winit::event::WindowEvent::Resized(size),//ignoring scale factor changed for now because mutex bruh + window_id:_, + } => { + window_thread.send(TimedInstruction{time,instruction:WindowInstruction::Resize(size)}).unwrap(); + } + winit::event::Event::WindowEvent{event,..}=>match event{ + winit::event::WindowEvent::KeyboardInput{ + event: + winit::event::KeyEvent { + logical_key: winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } + |winit::event::WindowEvent::CloseRequested=>{ + elwt.exit(); + } + winit::event::WindowEvent::RedrawRequested=>{ + window_thread.send(TimedInstruction{time,instruction:WindowInstruction::Render}).unwrap(); + } + _=>{ + window_thread.send(TimedInstruction{time,instruction:WindowInstruction::WindowEvent(event)}).unwrap(); + } + }, + winit::event::Event::DeviceEvent{ + event, + .. + } => { + window_thread.send(TimedInstruction{time,instruction:WindowInstruction::DeviceEvent(event)}).unwrap(); + }, + _=>{} + } + }) +} \ No newline at end of file diff --git a/src/window.rs b/src/window.rs new file mode 100644 index 0000000..e53dc18 --- /dev/null +++ b/src/window.rs @@ -0,0 +1,243 @@ +use crate::instruction::TimedInstruction; +use crate::physics_worker::InputInstruction; + +pub enum WindowInstruction{ + Resize(winit::dpi::PhysicalSize), + WindowEvent(winit::event::WindowEvent), + DeviceEvent(winit::event::DeviceEvent), + RequestRedraw, + Render, +} + +//holds thread handles to dispatch to +struct WindowContext<'a>{ + manual_mouse_lock:bool, + mouse:crate::physics::MouseState,//std::sync::Arc> + screen_size:glam::UVec2, + user_settings:crate::settings::UserSettings, + window:winit::window::Window, + physics_thread:crate::compat_worker::QNWorker<'a, TimedInstruction>, +} + +impl WindowContext<'_>{ + fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition{ + winit::dpi::PhysicalPosition::new(self.screen_size.x as f32/2.0, self.screen_size.y as f32/2.0) + } + fn window_event(&mut self,time:crate::integer::Time,event: winit::event::WindowEvent) { + match event { + winit::event::WindowEvent::DroppedFile(path)=>{ + //blocking because it's simpler... + if let Some(indexed_model_instances)=crate::load_file(path){ + self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ClearModels}).unwrap(); + self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::GenerateModels(indexed_model_instances)}).unwrap(); + } + }, + winit::event::WindowEvent::Focused(state)=>{ + //pause unpause + //recalculate pressed keys on focus + }, + winit::event::WindowEvent::KeyboardInput{ + event:winit::event::KeyEvent{state,logical_key,repeat:false,..}, + .. + }=>{ + let s=match state{ + winit::event::ElementState::Pressed=>true, + winit::event::ElementState::Released=>false, + }; + match logical_key{ + winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab)=>{ + if s{ + self.manual_mouse_lock=false; + match self.window.set_cursor_position(self.get_middle_of_screen()){ + Ok(())=>(), + Err(e)=>println!("Could not set cursor position: {:?}",e), + } + match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){ + Ok(())=>(), + Err(e)=>println!("Could not release cursor: {:?}",e), + } + }else{ + //if cursor is outside window don't lock but apparently there's no get pos function + //let pos=window.get_cursor_pos(); + match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){ + Ok(())=>(), + Err(_)=>{ + match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){ + Ok(())=>(), + Err(e)=>{ + self.manual_mouse_lock=true; + println!("Could not confine cursor: {:?}",e) + }, + } + } + } + } + self.window.set_cursor_visible(s); + }, + winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11)=>{ + if s{ + if self.window.fullscreen().is_some(){ + self.window.set_fullscreen(None); + }else{ + self.window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); + } + } + }, + winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape)=>{ + if s{ + self.manual_mouse_lock=false; + match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){ + Ok(())=>(), + Err(e)=>println!("Could not release cursor: {:?}",e), + } + self.window.set_cursor_visible(true); + } + }, + keycode=>{ + if let Some(input_instruction)=match keycode{ + winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(InputInstruction::Jump(s)), + winit::keyboard::Key::Character(key)=>match key.as_str(){ + "w"=>Some(InputInstruction::MoveForward(s)), + "a"=>Some(InputInstruction::MoveLeft(s)), + "s"=>Some(InputInstruction::MoveBack(s)), + "d"=>Some(InputInstruction::MoveRight(s)), + "e"=>Some(InputInstruction::MoveUp(s)), + "q"=>Some(InputInstruction::MoveDown(s)), + "z"=>Some(InputInstruction::Zoom(s)), + "r"=>if s{Some(InputInstruction::Reset)}else{None}, + _=>None, + }, + _=>None, + }{ + self.physics_thread.send(TimedInstruction{ + time, + instruction:crate::physics_worker::Instruction::Input(input_instruction), + }).unwrap(); + } + }, + } + }, + _=>(), + } + } + + fn device_event(&mut self,time:crate::integer::Time,event: winit::event::DeviceEvent) { + match event { + winit::event::DeviceEvent::MouseMotion { + delta,//these (f64,f64) are integers on my machine + } => { + if self.manual_mouse_lock{ + match self.window.set_cursor_position(self.get_middle_of_screen()){ + Ok(())=>(), + Err(e)=>println!("Could not set cursor position: {:?}",e), + } + } + //do not step the physics because the mouse polling rate is higher than the physics can run. + //essentially the previous input will be overwritten until a true step runs + //which is fine because they run all the time. + let delta=glam::ivec2(delta.0 as i32,delta.1 as i32); + self.mouse.pos+=delta; + self.physics_thread.send(TimedInstruction{ + time, + instruction:crate::physics_worker::Instruction::Input(InputInstruction::MoveMouse(self.mouse.pos)), + }).unwrap(); + }, + winit::event::DeviceEvent::MouseWheel { + delta, + } => { + println!("mousewheel {:?}",delta); + if false{//self.physics.style.use_scroll{ + self.physics_thread.send(TimedInstruction{ + time, + instruction:crate::physics_worker::Instruction::Input(InputInstruction::Jump(true)),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump + }).unwrap(); + } + } + _=>(), + } + } +} + +pub struct WindowContextSetup{ + user_settings:crate::settings::UserSettings, + window:winit::window::Window, + physics:crate::physics::PhysicsState, + graphics:crate::graphics::GraphicsState, +} + +impl WindowContextSetup{ + pub fn new(context:&crate::setup::SetupContext,window:winit::window::Window)->Self{ + //wee + let user_settings=crate::settings::read_user_settings(); + + let args:Vec=std::env::args().collect(); + let indexed_model_instances=if args.len()==2{ + crate::load_file(std::path::PathBuf::from(&args[1])) + }else{ + None + }.unwrap_or(crate::default_models()); + + let mut physics=crate::physics::PhysicsState::default(); + physics.load_user_settings(&user_settings); + physics.generate_models(&indexed_model_instances); + physics.spawn(indexed_model_instances.spawn_point); + + let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue,&context.config); + graphics.load_user_settings(&user_settings); + graphics.generate_models(&context.device,&context.queue,indexed_model_instances); + + Self{ + user_settings, + window, + graphics, + physics, + } + } + + fn into_context<'a>(self,setup_context:crate::setup::SetupContext)->WindowContext<'a>{ + let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height); + let graphics_thread=crate::graphics_worker::new(self.graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue); + WindowContext{ + manual_mouse_lock:false, + mouse:crate::physics::MouseState::default(), + //make sure to update this!!!!! + screen_size, + user_settings:self.user_settings, + window:self.window, + physics_thread:crate::physics_worker::new(self.physics,graphics_thread), + } + } + + pub fn into_worker<'a>(self,setup_context:crate::setup::SetupContext)->crate::compat_worker::QNWorker<'a,TimedInstruction>{ + let mut window_context=self.into_context(setup_context); + crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ + match ins.instruction{ + WindowInstruction::RequestRedraw=>{ + window_context.window.request_redraw(); + } + WindowInstruction::WindowEvent(window_event)=>{ + window_context.window_event(ins.time,window_event); + }, + WindowInstruction::DeviceEvent(device_event)=>{ + window_context.device_event(ins.time,device_event); + }, + WindowInstruction::Resize(size)=>{ + window_context.physics_thread.send( + TimedInstruction{ + time:ins.time, + instruction:crate::physics_worker::Instruction::Resize(size,window_context.user_settings.clone()) + } + ).unwrap(); + } + WindowInstruction::Render=>{ + window_context.physics_thread.send( + TimedInstruction{ + time:ins.time, + instruction:crate::physics_worker::Instruction::Render + } + ).unwrap(); + } + } + }) + } +} \ No newline at end of file diff --git a/src/worker.rs b/src/worker.rs index 863c982..a334681 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -1,17 +1,69 @@ use std::thread; use std::sync::{mpsc,Arc}; -use parking_lot::Mutex; +use parking_lot::{Mutex,Condvar}; + +//WorkerPool +struct Pool(u32); +enum PoolOrdering{ + Single,//single thread cannot get out of order + Ordered(u32),//order matters and should be buffered/dropped according to ControlFlow + Unordered(u32),//order does not matter +} +//WorkerInput +enum Input{ + //no input, workers have everything needed at creation + None, + //Immediate input to any available worker, dropped if they are overflowing (all workers are busy) + Immediate, + //Queued input is ordered, but serial jobs that mutate state (such as running physics) can only be done with a single worker + Queued,//"Fifo" + //Query a function to get next input when a thread becomes available + //worker stops querying when Query function returns None and dies after all threads complete + //lifetimes sound crazy on this one + Query, + //Queue of length one, the input is replaced if it is submitted twice before the current work finishes + Mailbox, +} +//WorkerOutput +enum Output{ + None(Pool), + Realtime(PoolOrdering),//outputs are dropped if they are out of order and order is demanded + Buffered(PoolOrdering),//outputs are held back internally if they are out of order and order is demanded +} + +//It would be possible to implement all variants +//with a query input function and callback output function but I'm not sure if that's worth it. +//Immediate = Condvar +//Queued = receiver.recv() +//a callback function would need to use an async runtime! + +//realtime output is an arc mutex of the output value that is assigned every time a worker completes a job +//buffered output produces a receiver object that can be passed to the creation of another worker +//when ordering is requested, output is ordered by the order each thread is run +//which is the same as the order that the input data is processed except for Input::None which has no input data +//WorkerDescription +struct Description{ + input:Input, + output:Output, +} //The goal here is to have a worker thread that parks itself when it runs out of work. //The worker thread publishes the result of its work back to the worker object for every item in the work queue. +//Previous values do not matter as soon as a new value is produced, which is why it's called "Realtime" //The physics (target use case) knows when it has not changed the body, so not updating the value is also an option. -pub struct Worker { +/* +QR = WorkerDescription{ + input:Queued, + output:Realtime(Single), +} +*/ +pub struct QRWorker{ sender: mpsc::Sender, value:Arc>, } -impl Worker { +impl QRWorker{ pub fn newValue+Send+'static>(value:Value,mut f:F) -> Self { let (sender, receiver) = mpsc::channel::(); let ret=Self { @@ -45,28 +97,79 @@ impl Worker { } } -pub struct CompatWorker{ - data:std::marker::PhantomData, - f:F, - value:Value, +/* +QN = WorkerDescription{ + input:Queued, + output:None(Single), +} +*/ +//None Output Worker does all its work internally from the perspective of the work submitter +pub struct QNWorker<'a,Task:Send>{ + sender: mpsc::Sender, + handle:thread::ScopedJoinHandle<'a,()>, } -implValue> CompatWorker { - pub fn new(value:Value,f:F) -> Self { - Self { - f, - value, - data:std::marker::PhantomData, +impl<'a,Task:Send+'a> QNWorker<'a,Task>{ + pub fn new(scope:&'a thread::Scope<'a,'_>,mut f:F)->QNWorker<'a,Task>{ + let (sender,receiver)=mpsc::channel::(); + let handle=scope.spawn(move ||{ + loop { + match receiver.recv() { + Ok(task)=>f(task), + Err(_)=>{ + println!("Worker stopping.",); + break; + } + } + } + }); + Self{ + sender, + handle, } } - - pub fn send(&mut self,task:Task)->Result<(),()>{ - self.value=(self.f)(task); - Ok(()) + pub fn send(&self,task:Task)->Result<(),mpsc::SendError>{ + self.sender.send(task) } +} - pub fn grab_clone(&self)->Value{ - self.value.clone() +/* +IN = WorkerDescription{ + input:Immediate, + output:None(Single), +} +*/ +//Inputs are dropped if the worker is busy +pub struct INWorker<'a,Task:Send>{ + sender: mpsc::SyncSender, + handle:thread::ScopedJoinHandle<'a,()>, +} + +impl<'a,Task:Send+'a> INWorker<'a,Task>{ + pub fn new(scope:&'a thread::Scope<'a,'_>,mut f:F)->INWorker<'a,Task>{ + let (sender,receiver)=mpsc::sync_channel::(1); + let handle=scope.spawn(move ||{ + loop { + match receiver.recv() { + Ok(task)=>f(task), + Err(_)=>{ + println!("Worker stopping.",); + break; + } + } + } + }); + Self{ + sender, + handle, + } + } + //blocking! + pub fn blocking_send(&self,task:Task)->Result<(), mpsc::SendError>{ + self.sender.send(task) + } + pub fn send(&self,task:Task)->Result<(), mpsc::TrySendError>{ + self.sender.try_send(task) } } @@ -74,7 +177,7 @@ implValue> CompatWorker { fn test_worker() { println!("hiiiii"); // Create the worker thread - let worker = Worker::new(crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO), + let worker=QRWorker::new(crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO,crate::integer::Planar64Vec3::ZERO), |_|crate::physics::Body::with_pva(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE) );