├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── archive ├── Cargo.toml └── src │ ├── archive_reader.rs │ ├── archive_writer.rs │ ├── constants.rs │ ├── errors.rs │ ├── lib.rs │ └── structures.rs ├── cli ├── Cargo.toml └── src │ └── main.rs └── player ├── Cargo.toml ├── shaders ├── scale.wgsl └── texture_update_by_coords.compute.wgsl └── src ├── lib.rs ├── pixel_art_display_state.rs ├── renderers.rs ├── texture_update_by_coords.rs └── transform_generator.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: push 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | 10 | - uses: actions-rs/toolchain@v1 11 | with: 12 | toolchain: stable 13 | 14 | - uses: Swatinem/rust-cache@v2 15 | 16 | - name: install swiftshader 17 | shell: bash 18 | run: | 19 | set -e 20 | mkdir -p swiftshader 21 | curl -LsSf https://github.com/gfx-rs/ci-build/releases/latest/download/swiftshader-linux-x86_64.tar.xz | tar -xf - -C swiftshader 22 | echo "LD_LIBRARY_PATH=$PWD/swiftshader" >> $GITHUB_ENV 23 | 24 | - name: install llvmpipe, vulkan sdk 25 | shell: bash 26 | run: | 27 | set -e 28 | sudo apt-get update -y -qq 29 | # vulkan sdk 30 | wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - 31 | sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list 32 | sudo apt-get update 33 | sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk 34 | 35 | - uses: actions-rs/cargo@v1 36 | with: 37 | command: test 38 | env: 39 | WGPU_BACKEND: vulkan 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.bin 3 | flamegraph.svg 4 | .DS_Store 5 | *.png 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.19.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aes" 22 | version = "0.8.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" 25 | dependencies = [ 26 | "cfg-if", 27 | "cipher", 28 | "cpufeatures", 29 | ] 30 | 31 | [[package]] 32 | name = "ahash" 33 | version = "0.7.6" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 36 | dependencies = [ 37 | "getrandom", 38 | "once_cell", 39 | "version_check", 40 | ] 41 | 42 | [[package]] 43 | name = "aho-corasick" 44 | version = "0.7.20" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 47 | dependencies = [ 48 | "memchr", 49 | ] 50 | 51 | [[package]] 52 | name = "alloc-no-stdlib" 53 | version = "2.0.4" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 56 | 57 | [[package]] 58 | name = "alloc-stdlib" 59 | version = "0.2.2" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 62 | dependencies = [ 63 | "alloc-no-stdlib", 64 | ] 65 | 66 | [[package]] 67 | name = "android_system_properties" 68 | version = "0.1.5" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 71 | dependencies = [ 72 | "libc", 73 | ] 74 | 75 | [[package]] 76 | name = "archive" 77 | version = "0.1.0" 78 | dependencies = [ 79 | "bincode 2.0.0-rc.2", 80 | "chrono", 81 | "colors-transform", 82 | "image", 83 | "mla", 84 | "rand", 85 | "tempfile", 86 | ] 87 | 88 | [[package]] 89 | name = "arrayref" 90 | version = "0.3.6" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 93 | 94 | [[package]] 95 | name = "arrayvec" 96 | version = "0.5.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 99 | 100 | [[package]] 101 | name = "arrayvec" 102 | version = "0.7.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 105 | 106 | [[package]] 107 | name = "ash" 108 | version = "0.37.2+1.3.238" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03" 111 | dependencies = [ 112 | "libloading", 113 | ] 114 | 115 | [[package]] 116 | name = "autocfg" 117 | version = "1.1.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 120 | 121 | [[package]] 122 | name = "backtrace" 123 | version = "0.3.67" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" 126 | dependencies = [ 127 | "addr2line", 128 | "cc", 129 | "cfg-if", 130 | "libc", 131 | "miniz_oxide 0.6.2", 132 | "object", 133 | "rustc-demangle", 134 | ] 135 | 136 | [[package]] 137 | name = "bincode" 138 | version = "1.3.3" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 141 | dependencies = [ 142 | "serde", 143 | ] 144 | 145 | [[package]] 146 | name = "bincode" 147 | version = "2.0.0-rc.2" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4" 150 | dependencies = [ 151 | "bincode_derive", 152 | "serde", 153 | ] 154 | 155 | [[package]] 156 | name = "bincode_derive" 157 | version = "2.0.0-rc.2" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "0a45a23389446d2dd25dc8e73a7a3b3c43522b630cac068927f0649d43d719d2" 160 | dependencies = [ 161 | "virtue", 162 | ] 163 | 164 | [[package]] 165 | name = "bit-set" 166 | version = "0.5.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 169 | dependencies = [ 170 | "bit-vec", 171 | ] 172 | 173 | [[package]] 174 | name = "bit-vec" 175 | version = "0.6.3" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 178 | 179 | [[package]] 180 | name = "bit_field" 181 | version = "0.10.1" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 184 | 185 | [[package]] 186 | name = "bitflags" 187 | version = "1.3.2" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 190 | 191 | [[package]] 192 | name = "block" 193 | version = "0.1.6" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 196 | 197 | [[package]] 198 | name = "block-buffer" 199 | version = "0.10.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 202 | dependencies = [ 203 | "generic-array", 204 | ] 205 | 206 | [[package]] 207 | name = "brotli" 208 | version = "3.3.4" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" 211 | dependencies = [ 212 | "alloc-no-stdlib", 213 | "alloc-stdlib", 214 | "brotli-decompressor", 215 | ] 216 | 217 | [[package]] 218 | name = "brotli-decompressor" 219 | version = "2.3.2" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" 222 | dependencies = [ 223 | "alloc-no-stdlib", 224 | "alloc-stdlib", 225 | ] 226 | 227 | [[package]] 228 | name = "bstr" 229 | version = "0.2.17" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 232 | dependencies = [ 233 | "lazy_static", 234 | "memchr", 235 | "regex-automata", 236 | "serde", 237 | ] 238 | 239 | [[package]] 240 | name = "bumpalo" 241 | version = "3.11.1" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 244 | 245 | [[package]] 246 | name = "byte-unit" 247 | version = "4.0.18" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484" 250 | dependencies = [ 251 | "serde", 252 | "utf8-width", 253 | ] 254 | 255 | [[package]] 256 | name = "bytemuck" 257 | version = "1.12.3" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" 260 | 261 | [[package]] 262 | name = "byteorder" 263 | version = "1.4.3" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 266 | 267 | [[package]] 268 | name = "calloop" 269 | version = "0.10.5" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" 272 | dependencies = [ 273 | "log", 274 | "nix 0.25.1", 275 | "slotmap", 276 | "thiserror", 277 | "vec_map", 278 | ] 279 | 280 | [[package]] 281 | name = "cc" 282 | version = "1.0.76" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" 285 | 286 | [[package]] 287 | name = "cfg-if" 288 | version = "1.0.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 291 | 292 | [[package]] 293 | name = "chrono" 294 | version = "0.4.23" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" 297 | dependencies = [ 298 | "iana-time-zone", 299 | "js-sys", 300 | "num-integer", 301 | "num-traits", 302 | "time", 303 | "wasm-bindgen", 304 | "winapi", 305 | ] 306 | 307 | [[package]] 308 | name = "cipher" 309 | version = "0.4.3" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" 312 | dependencies = [ 313 | "crypto-common", 314 | "inout", 315 | ] 316 | 317 | [[package]] 318 | name = "clap" 319 | version = "4.0.29" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" 322 | dependencies = [ 323 | "bitflags", 324 | "clap_derive", 325 | "clap_lex", 326 | "is-terminal", 327 | "once_cell", 328 | "strsim", 329 | "termcolor", 330 | ] 331 | 332 | [[package]] 333 | name = "clap_derive" 334 | version = "4.0.21" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" 337 | dependencies = [ 338 | "heck", 339 | "proc-macro-error", 340 | "proc-macro2", 341 | "quote", 342 | "syn", 343 | ] 344 | 345 | [[package]] 346 | name = "clap_lex" 347 | version = "0.3.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 350 | dependencies = [ 351 | "os_str_bytes", 352 | ] 353 | 354 | [[package]] 355 | name = "cli" 356 | version = "0.1.0" 357 | dependencies = [ 358 | "archive", 359 | "byte-unit", 360 | "chrono", 361 | "clap", 362 | "colors-transform", 363 | "csv", 364 | "image", 365 | "player", 366 | "proc-macro2", 367 | ] 368 | 369 | [[package]] 370 | name = "cmake" 371 | version = "0.1.49" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" 374 | dependencies = [ 375 | "cc", 376 | ] 377 | 378 | [[package]] 379 | name = "cocoa" 380 | version = "0.24.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" 383 | dependencies = [ 384 | "bitflags", 385 | "block", 386 | "cocoa-foundation", 387 | "core-foundation", 388 | "core-graphics", 389 | "foreign-types 0.3.2", 390 | "libc", 391 | "objc", 392 | ] 393 | 394 | [[package]] 395 | name = "cocoa-foundation" 396 | version = "0.1.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" 399 | dependencies = [ 400 | "bitflags", 401 | "block", 402 | "core-foundation", 403 | "core-graphics-types", 404 | "foreign-types 0.3.2", 405 | "libc", 406 | "objc", 407 | ] 408 | 409 | [[package]] 410 | name = "codespan-reporting" 411 | version = "0.11.1" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 414 | dependencies = [ 415 | "termcolor", 416 | "unicode-width", 417 | ] 418 | 419 | [[package]] 420 | name = "color_quant" 421 | version = "1.1.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 424 | 425 | [[package]] 426 | name = "colors-transform" 427 | version = "0.2.11" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" 430 | 431 | [[package]] 432 | name = "com-rs" 433 | version = "0.2.1" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" 436 | 437 | [[package]] 438 | name = "core-foundation" 439 | version = "0.9.3" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 442 | dependencies = [ 443 | "core-foundation-sys", 444 | "libc", 445 | ] 446 | 447 | [[package]] 448 | name = "core-foundation-sys" 449 | version = "0.8.3" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 452 | 453 | [[package]] 454 | name = "core-graphics" 455 | version = "0.22.3" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" 458 | dependencies = [ 459 | "bitflags", 460 | "core-foundation", 461 | "core-graphics-types", 462 | "foreign-types 0.3.2", 463 | "libc", 464 | ] 465 | 466 | [[package]] 467 | name = "core-graphics-types" 468 | version = "0.1.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 471 | dependencies = [ 472 | "bitflags", 473 | "core-foundation", 474 | "foreign-types 0.3.2", 475 | "libc", 476 | ] 477 | 478 | [[package]] 479 | name = "core-text" 480 | version = "19.2.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" 483 | dependencies = [ 484 | "core-foundation", 485 | "core-graphics", 486 | "foreign-types 0.3.2", 487 | "libc", 488 | ] 489 | 490 | [[package]] 491 | name = "cpufeatures" 492 | version = "0.2.5" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 495 | dependencies = [ 496 | "libc", 497 | ] 498 | 499 | [[package]] 500 | name = "crc32fast" 501 | version = "1.3.2" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 504 | dependencies = [ 505 | "cfg-if", 506 | ] 507 | 508 | [[package]] 509 | name = "crossbeam-channel" 510 | version = "0.5.6" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" 513 | dependencies = [ 514 | "cfg-if", 515 | "crossbeam-utils", 516 | ] 517 | 518 | [[package]] 519 | name = "crossbeam-deque" 520 | version = "0.8.2" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" 523 | dependencies = [ 524 | "cfg-if", 525 | "crossbeam-epoch", 526 | "crossbeam-utils", 527 | ] 528 | 529 | [[package]] 530 | name = "crossbeam-epoch" 531 | version = "0.9.11" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" 534 | dependencies = [ 535 | "autocfg", 536 | "cfg-if", 537 | "crossbeam-utils", 538 | "memoffset", 539 | "scopeguard", 540 | ] 541 | 542 | [[package]] 543 | name = "crossbeam-utils" 544 | version = "0.8.12" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" 547 | dependencies = [ 548 | "cfg-if", 549 | ] 550 | 551 | [[package]] 552 | name = "crossfont" 553 | version = "0.5.1" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" 556 | dependencies = [ 557 | "cocoa", 558 | "core-foundation", 559 | "core-foundation-sys", 560 | "core-graphics", 561 | "core-text", 562 | "dwrote", 563 | "foreign-types 0.5.0", 564 | "freetype-rs", 565 | "libc", 566 | "log", 567 | "objc", 568 | "once_cell", 569 | "pkg-config", 570 | "servo-fontconfig", 571 | "winapi", 572 | ] 573 | 574 | [[package]] 575 | name = "crunchy" 576 | version = "0.2.2" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 579 | 580 | [[package]] 581 | name = "crypto-common" 582 | version = "0.1.6" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 585 | dependencies = [ 586 | "generic-array", 587 | "typenum", 588 | ] 589 | 590 | [[package]] 591 | name = "csv" 592 | version = "1.1.6" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 595 | dependencies = [ 596 | "bstr", 597 | "csv-core", 598 | "itoa", 599 | "ryu", 600 | "serde", 601 | ] 602 | 603 | [[package]] 604 | name = "csv-core" 605 | version = "0.1.10" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 608 | dependencies = [ 609 | "memchr", 610 | ] 611 | 612 | [[package]] 613 | name = "ctr" 614 | version = "0.9.2" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" 617 | dependencies = [ 618 | "cipher", 619 | ] 620 | 621 | [[package]] 622 | name = "cty" 623 | version = "0.2.2" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 626 | 627 | [[package]] 628 | name = "curve25519-dalek" 629 | version = "3.2.1" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" 632 | dependencies = [ 633 | "byteorder", 634 | "digest 0.9.0", 635 | "rand_core 0.5.1", 636 | "subtle", 637 | "zeroize", 638 | ] 639 | 640 | [[package]] 641 | name = "cxx" 642 | version = "1.0.82" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" 645 | dependencies = [ 646 | "cc", 647 | "cxxbridge-flags", 648 | "cxxbridge-macro", 649 | "link-cplusplus", 650 | ] 651 | 652 | [[package]] 653 | name = "cxx-build" 654 | version = "1.0.82" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" 657 | dependencies = [ 658 | "cc", 659 | "codespan-reporting", 660 | "once_cell", 661 | "proc-macro2", 662 | "quote", 663 | "scratch", 664 | "syn", 665 | ] 666 | 667 | [[package]] 668 | name = "cxxbridge-flags" 669 | version = "1.0.82" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" 672 | 673 | [[package]] 674 | name = "cxxbridge-macro" 675 | version = "1.0.82" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" 678 | dependencies = [ 679 | "proc-macro2", 680 | "quote", 681 | "syn", 682 | ] 683 | 684 | [[package]] 685 | name = "d3d12" 686 | version = "0.6.0" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da" 689 | dependencies = [ 690 | "bitflags", 691 | "libloading", 692 | "winapi", 693 | ] 694 | 695 | [[package]] 696 | name = "darling" 697 | version = "0.13.4" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" 700 | dependencies = [ 701 | "darling_core", 702 | "darling_macro", 703 | ] 704 | 705 | [[package]] 706 | name = "darling_core" 707 | version = "0.13.4" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" 710 | dependencies = [ 711 | "fnv", 712 | "ident_case", 713 | "proc-macro2", 714 | "quote", 715 | "strsim", 716 | "syn", 717 | ] 718 | 719 | [[package]] 720 | name = "darling_macro" 721 | version = "0.13.4" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" 724 | dependencies = [ 725 | "darling_core", 726 | "quote", 727 | "syn", 728 | ] 729 | 730 | [[package]] 731 | name = "digest" 732 | version = "0.9.0" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 735 | dependencies = [ 736 | "generic-array", 737 | ] 738 | 739 | [[package]] 740 | name = "digest" 741 | version = "0.10.6" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 744 | dependencies = [ 745 | "block-buffer", 746 | "crypto-common", 747 | "subtle", 748 | ] 749 | 750 | [[package]] 751 | name = "dispatch" 752 | version = "0.2.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 755 | 756 | [[package]] 757 | name = "dlib" 758 | version = "0.5.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" 761 | dependencies = [ 762 | "libloading", 763 | ] 764 | 765 | [[package]] 766 | name = "downcast-rs" 767 | version = "1.2.0" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 770 | 771 | [[package]] 772 | name = "dwrote" 773 | version = "0.11.0" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" 776 | dependencies = [ 777 | "lazy_static", 778 | "libc", 779 | "serde", 780 | "serde_derive", 781 | "winapi", 782 | "wio", 783 | ] 784 | 785 | [[package]] 786 | name = "either" 787 | version = "1.8.0" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 790 | 791 | [[package]] 792 | name = "env_logger" 793 | version = "0.10.0" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 796 | dependencies = [ 797 | "humantime", 798 | "is-terminal", 799 | "log", 800 | "regex", 801 | "termcolor", 802 | ] 803 | 804 | [[package]] 805 | name = "errno" 806 | version = "0.2.8" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 809 | dependencies = [ 810 | "errno-dragonfly", 811 | "libc", 812 | "winapi", 813 | ] 814 | 815 | [[package]] 816 | name = "errno-dragonfly" 817 | version = "0.1.2" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 820 | dependencies = [ 821 | "cc", 822 | "libc", 823 | ] 824 | 825 | [[package]] 826 | name = "expat-sys" 827 | version = "2.1.6" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" 830 | dependencies = [ 831 | "cmake", 832 | "pkg-config", 833 | ] 834 | 835 | [[package]] 836 | name = "exr" 837 | version = "1.5.2" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded" 840 | dependencies = [ 841 | "bit_field", 842 | "flume", 843 | "half", 844 | "lebe", 845 | "miniz_oxide 0.6.2", 846 | "smallvec", 847 | "threadpool", 848 | ] 849 | 850 | [[package]] 851 | name = "fastrand" 852 | version = "1.8.0" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 855 | dependencies = [ 856 | "instant", 857 | ] 858 | 859 | [[package]] 860 | name = "flate2" 861 | version = "1.0.24" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 864 | dependencies = [ 865 | "crc32fast", 866 | "miniz_oxide 0.5.4", 867 | ] 868 | 869 | [[package]] 870 | name = "flume" 871 | version = "0.10.14" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" 874 | dependencies = [ 875 | "futures-core", 876 | "futures-sink", 877 | "nanorand", 878 | "pin-project", 879 | "spin", 880 | ] 881 | 882 | [[package]] 883 | name = "fnv" 884 | version = "1.0.7" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 887 | 888 | [[package]] 889 | name = "foreign-types" 890 | version = "0.3.2" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 893 | dependencies = [ 894 | "foreign-types-shared 0.1.1", 895 | ] 896 | 897 | [[package]] 898 | name = "foreign-types" 899 | version = "0.5.0" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 902 | dependencies = [ 903 | "foreign-types-macros", 904 | "foreign-types-shared 0.3.1", 905 | ] 906 | 907 | [[package]] 908 | name = "foreign-types-macros" 909 | version = "0.2.2" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" 912 | dependencies = [ 913 | "proc-macro2", 914 | "quote", 915 | "syn", 916 | ] 917 | 918 | [[package]] 919 | name = "foreign-types-shared" 920 | version = "0.1.1" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 923 | 924 | [[package]] 925 | name = "foreign-types-shared" 926 | version = "0.3.1" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 929 | 930 | [[package]] 931 | name = "freetype-rs" 932 | version = "0.26.0" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" 935 | dependencies = [ 936 | "bitflags", 937 | "freetype-sys", 938 | "libc", 939 | ] 940 | 941 | [[package]] 942 | name = "freetype-sys" 943 | version = "0.13.1" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" 946 | dependencies = [ 947 | "cmake", 948 | "libc", 949 | "pkg-config", 950 | ] 951 | 952 | [[package]] 953 | name = "futures-core" 954 | version = "0.3.25" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 957 | 958 | [[package]] 959 | name = "futures-intrusive" 960 | version = "0.5.0" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 963 | dependencies = [ 964 | "futures-core", 965 | "lock_api", 966 | "parking_lot", 967 | ] 968 | 969 | [[package]] 970 | name = "futures-sink" 971 | version = "0.3.25" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" 974 | 975 | [[package]] 976 | name = "fxhash" 977 | version = "0.2.1" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 980 | dependencies = [ 981 | "byteorder", 982 | ] 983 | 984 | [[package]] 985 | name = "game-loop" 986 | version = "0.10.0" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "fdd62d243b7fde7998a255fd44b7435829f28b0e3b636ab1050fea5c7db92ce5" 989 | dependencies = [ 990 | "wasm-bindgen", 991 | "web-sys", 992 | "winit", 993 | ] 994 | 995 | [[package]] 996 | name = "generic-array" 997 | version = "0.14.6" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 1000 | dependencies = [ 1001 | "typenum", 1002 | "version_check", 1003 | ] 1004 | 1005 | [[package]] 1006 | name = "getrandom" 1007 | version = "0.2.8" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 1010 | dependencies = [ 1011 | "cfg-if", 1012 | "js-sys", 1013 | "libc", 1014 | "wasi 0.11.0+wasi-snapshot-preview1", 1015 | "wasm-bindgen", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "ghash" 1020 | version = "0.5.0" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" 1023 | dependencies = [ 1024 | "opaque-debug", 1025 | "polyval", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "gif" 1030 | version = "0.11.4" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" 1033 | dependencies = [ 1034 | "color_quant", 1035 | "weezl", 1036 | ] 1037 | 1038 | [[package]] 1039 | name = "gimli" 1040 | version = "0.27.1" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" 1043 | 1044 | [[package]] 1045 | name = "glow" 1046 | version = "0.12.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c" 1049 | dependencies = [ 1050 | "js-sys", 1051 | "slotmap", 1052 | "wasm-bindgen", 1053 | "web-sys", 1054 | ] 1055 | 1056 | [[package]] 1057 | name = "gpu-alloc" 1058 | version = "0.5.3" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" 1061 | dependencies = [ 1062 | "bitflags", 1063 | "gpu-alloc-types", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "gpu-alloc-types" 1068 | version = "0.2.0" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5" 1071 | dependencies = [ 1072 | "bitflags", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "gpu-allocator" 1077 | version = "0.21.0" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "434618454f74b63f9b39328298097256977c41ea0ba9d75a47238b77790b6163" 1080 | dependencies = [ 1081 | "backtrace", 1082 | "log", 1083 | "thiserror", 1084 | "winapi", 1085 | "windows", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "gpu-descriptor" 1090 | version = "0.2.3" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" 1093 | dependencies = [ 1094 | "bitflags", 1095 | "gpu-descriptor-types", 1096 | "hashbrown", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "gpu-descriptor-types" 1101 | version = "0.1.1" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126" 1104 | dependencies = [ 1105 | "bitflags", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "half" 1110 | version = "2.1.0" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" 1113 | dependencies = [ 1114 | "crunchy", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "hashbrown" 1119 | version = "0.12.3" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1122 | dependencies = [ 1123 | "ahash", 1124 | ] 1125 | 1126 | [[package]] 1127 | name = "hassle-rs" 1128 | version = "0.9.0" 1129 | source = "registry+https://github.com/rust-lang/crates.io-index" 1130 | checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85" 1131 | dependencies = [ 1132 | "bitflags", 1133 | "com-rs", 1134 | "libc", 1135 | "libloading", 1136 | "thiserror", 1137 | "widestring", 1138 | "winapi", 1139 | ] 1140 | 1141 | [[package]] 1142 | name = "heck" 1143 | version = "0.4.0" 1144 | source = "registry+https://github.com/rust-lang/crates.io-index" 1145 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 1146 | 1147 | [[package]] 1148 | name = "hermit-abi" 1149 | version = "0.1.19" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 1152 | dependencies = [ 1153 | "libc", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "hermit-abi" 1158 | version = "0.2.6" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 1161 | dependencies = [ 1162 | "libc", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "hexf-parse" 1167 | version = "0.2.1" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 1170 | 1171 | [[package]] 1172 | name = "hkdf" 1173 | version = "0.12.3" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" 1176 | dependencies = [ 1177 | "hmac", 1178 | ] 1179 | 1180 | [[package]] 1181 | name = "hmac" 1182 | version = "0.12.1" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 1185 | dependencies = [ 1186 | "digest 0.10.6", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "humantime" 1191 | version = "2.1.0" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 1194 | 1195 | [[package]] 1196 | name = "iana-time-zone" 1197 | version = "0.1.53" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 1200 | dependencies = [ 1201 | "android_system_properties", 1202 | "core-foundation-sys", 1203 | "iana-time-zone-haiku", 1204 | "js-sys", 1205 | "wasm-bindgen", 1206 | "winapi", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "iana-time-zone-haiku" 1211 | version = "0.1.1" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 1214 | dependencies = [ 1215 | "cxx", 1216 | "cxx-build", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "ident_case" 1221 | version = "1.0.1" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1224 | 1225 | [[package]] 1226 | name = "image" 1227 | version = "0.24.5" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" 1230 | dependencies = [ 1231 | "bytemuck", 1232 | "byteorder", 1233 | "color_quant", 1234 | "exr", 1235 | "gif", 1236 | "jpeg-decoder", 1237 | "num-rational", 1238 | "num-traits", 1239 | "png", 1240 | "scoped_threadpool", 1241 | "tiff", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "indexmap" 1246 | version = "1.9.2" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 1249 | dependencies = [ 1250 | "autocfg", 1251 | "hashbrown", 1252 | ] 1253 | 1254 | [[package]] 1255 | name = "inout" 1256 | version = "0.1.3" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 1259 | dependencies = [ 1260 | "generic-array", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "instant" 1265 | version = "0.1.12" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 1268 | dependencies = [ 1269 | "cfg-if", 1270 | "js-sys", 1271 | "wasm-bindgen", 1272 | "web-sys", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "io-lifetimes" 1277 | version = "1.0.3" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" 1280 | dependencies = [ 1281 | "libc", 1282 | "windows-sys 0.42.0", 1283 | ] 1284 | 1285 | [[package]] 1286 | name = "is-terminal" 1287 | version = "0.4.1" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" 1290 | dependencies = [ 1291 | "hermit-abi 0.2.6", 1292 | "io-lifetimes", 1293 | "rustix", 1294 | "windows-sys 0.42.0", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "itoa" 1299 | version = "0.4.8" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 1302 | 1303 | [[package]] 1304 | name = "jni-sys" 1305 | version = "0.3.0" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 1308 | 1309 | [[package]] 1310 | name = "jpeg-decoder" 1311 | version = "0.3.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" 1314 | dependencies = [ 1315 | "rayon", 1316 | ] 1317 | 1318 | [[package]] 1319 | name = "js-sys" 1320 | version = "0.3.60" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 1323 | dependencies = [ 1324 | "wasm-bindgen", 1325 | ] 1326 | 1327 | [[package]] 1328 | name = "khronos-egl" 1329 | version = "4.1.0" 1330 | source = "registry+https://github.com/rust-lang/crates.io-index" 1331 | checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" 1332 | dependencies = [ 1333 | "libc", 1334 | "libloading", 1335 | "pkg-config", 1336 | ] 1337 | 1338 | [[package]] 1339 | name = "lazy_static" 1340 | version = "1.4.0" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1343 | 1344 | [[package]] 1345 | name = "lebe" 1346 | version = "0.5.2" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" 1349 | 1350 | [[package]] 1351 | name = "libc" 1352 | version = "0.2.137" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 1355 | 1356 | [[package]] 1357 | name = "libloading" 1358 | version = "0.7.4" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 1361 | dependencies = [ 1362 | "cfg-if", 1363 | "winapi", 1364 | ] 1365 | 1366 | [[package]] 1367 | name = "link-cplusplus" 1368 | version = "1.0.7" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" 1371 | dependencies = [ 1372 | "cc", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "linux-raw-sys" 1377 | version = "0.1.3" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" 1380 | 1381 | [[package]] 1382 | name = "lock_api" 1383 | version = "0.4.9" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 1386 | dependencies = [ 1387 | "autocfg", 1388 | "scopeguard", 1389 | ] 1390 | 1391 | [[package]] 1392 | name = "log" 1393 | version = "0.4.17" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 1396 | dependencies = [ 1397 | "cfg-if", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "malloc_buf" 1402 | version = "0.0.6" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 1405 | dependencies = [ 1406 | "libc", 1407 | ] 1408 | 1409 | [[package]] 1410 | name = "memchr" 1411 | version = "2.5.0" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 1414 | 1415 | [[package]] 1416 | name = "memmap2" 1417 | version = "0.5.8" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" 1420 | dependencies = [ 1421 | "libc", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "memoffset" 1426 | version = "0.6.5" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 1429 | dependencies = [ 1430 | "autocfg", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "metal" 1435 | version = "0.24.0" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060" 1438 | dependencies = [ 1439 | "bitflags", 1440 | "block", 1441 | "core-graphics-types", 1442 | "foreign-types 0.3.2", 1443 | "log", 1444 | "objc", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "minimal-lexical" 1449 | version = "0.2.1" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1452 | 1453 | [[package]] 1454 | name = "miniz_oxide" 1455 | version = "0.5.4" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" 1458 | dependencies = [ 1459 | "adler", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "miniz_oxide" 1464 | version = "0.6.2" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 1467 | dependencies = [ 1468 | "adler", 1469 | ] 1470 | 1471 | [[package]] 1472 | name = "mio" 1473 | version = "0.8.5" 1474 | source = "registry+https://github.com/rust-lang/crates.io-index" 1475 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 1476 | dependencies = [ 1477 | "libc", 1478 | "log", 1479 | "wasi 0.11.0+wasi-snapshot-preview1", 1480 | "windows-sys 0.42.0", 1481 | ] 1482 | 1483 | [[package]] 1484 | name = "mla" 1485 | version = "1.3.0" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "c371b6694be13883d945d00804a73891bbbca26f1d7096e270f2886d48dc11f7" 1488 | dependencies = [ 1489 | "aes", 1490 | "bincode 1.3.3", 1491 | "bitflags", 1492 | "brotli", 1493 | "byteorder", 1494 | "ctr", 1495 | "digest 0.10.6", 1496 | "generic-array", 1497 | "ghash", 1498 | "hkdf", 1499 | "rand", 1500 | "rand_chacha", 1501 | "serde", 1502 | "sha2", 1503 | "subtle", 1504 | "x25519-dalek", 1505 | "zeroize", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "naga" 1510 | version = "0.11.0" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5" 1513 | dependencies = [ 1514 | "bit-set", 1515 | "bitflags", 1516 | "codespan-reporting", 1517 | "hexf-parse", 1518 | "indexmap", 1519 | "log", 1520 | "num-traits", 1521 | "rustc-hash", 1522 | "spirv", 1523 | "termcolor", 1524 | "thiserror", 1525 | "unicode-xid", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "nanorand" 1530 | version = "0.7.0" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" 1533 | dependencies = [ 1534 | "getrandom", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "ndk" 1539 | version = "0.7.0" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" 1542 | dependencies = [ 1543 | "bitflags", 1544 | "jni-sys", 1545 | "ndk-sys", 1546 | "num_enum", 1547 | "raw-window-handle 0.5.0", 1548 | "thiserror", 1549 | ] 1550 | 1551 | [[package]] 1552 | name = "ndk-context" 1553 | version = "0.1.1" 1554 | source = "registry+https://github.com/rust-lang/crates.io-index" 1555 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 1556 | 1557 | [[package]] 1558 | name = "ndk-glue" 1559 | version = "0.7.0" 1560 | source = "registry+https://github.com/rust-lang/crates.io-index" 1561 | checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" 1562 | dependencies = [ 1563 | "libc", 1564 | "log", 1565 | "ndk", 1566 | "ndk-context", 1567 | "ndk-macro", 1568 | "ndk-sys", 1569 | "once_cell", 1570 | "parking_lot", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "ndk-macro" 1575 | version = "0.3.0" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" 1578 | dependencies = [ 1579 | "darling", 1580 | "proc-macro-crate", 1581 | "proc-macro2", 1582 | "quote", 1583 | "syn", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "ndk-sys" 1588 | version = "0.4.1+23.1.7779620" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" 1591 | dependencies = [ 1592 | "jni-sys", 1593 | ] 1594 | 1595 | [[package]] 1596 | name = "nix" 1597 | version = "0.24.3" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" 1600 | dependencies = [ 1601 | "bitflags", 1602 | "cfg-if", 1603 | "libc", 1604 | "memoffset", 1605 | ] 1606 | 1607 | [[package]] 1608 | name = "nix" 1609 | version = "0.25.1" 1610 | source = "registry+https://github.com/rust-lang/crates.io-index" 1611 | checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" 1612 | dependencies = [ 1613 | "autocfg", 1614 | "bitflags", 1615 | "cfg-if", 1616 | "libc", 1617 | "memoffset", 1618 | ] 1619 | 1620 | [[package]] 1621 | name = "nom" 1622 | version = "7.1.2" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" 1625 | dependencies = [ 1626 | "memchr", 1627 | "minimal-lexical", 1628 | ] 1629 | 1630 | [[package]] 1631 | name = "num" 1632 | version = "0.4.0" 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" 1634 | checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" 1635 | dependencies = [ 1636 | "num-bigint", 1637 | "num-complex", 1638 | "num-integer", 1639 | "num-iter", 1640 | "num-rational", 1641 | "num-traits", 1642 | ] 1643 | 1644 | [[package]] 1645 | name = "num-bigint" 1646 | version = "0.4.3" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 1649 | dependencies = [ 1650 | "autocfg", 1651 | "num-integer", 1652 | "num-traits", 1653 | ] 1654 | 1655 | [[package]] 1656 | name = "num-complex" 1657 | version = "0.4.3" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" 1660 | dependencies = [ 1661 | "num-traits", 1662 | ] 1663 | 1664 | [[package]] 1665 | name = "num-integer" 1666 | version = "0.1.45" 1667 | source = "registry+https://github.com/rust-lang/crates.io-index" 1668 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 1669 | dependencies = [ 1670 | "autocfg", 1671 | "num-traits", 1672 | ] 1673 | 1674 | [[package]] 1675 | name = "num-iter" 1676 | version = "0.1.43" 1677 | source = "registry+https://github.com/rust-lang/crates.io-index" 1678 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 1679 | dependencies = [ 1680 | "autocfg", 1681 | "num-integer", 1682 | "num-traits", 1683 | ] 1684 | 1685 | [[package]] 1686 | name = "num-rational" 1687 | version = "0.4.1" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 1690 | dependencies = [ 1691 | "autocfg", 1692 | "num-bigint", 1693 | "num-integer", 1694 | "num-traits", 1695 | ] 1696 | 1697 | [[package]] 1698 | name = "num-traits" 1699 | version = "0.2.15" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 1702 | dependencies = [ 1703 | "autocfg", 1704 | ] 1705 | 1706 | [[package]] 1707 | name = "num_cpus" 1708 | version = "1.14.0" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" 1711 | dependencies = [ 1712 | "hermit-abi 0.1.19", 1713 | "libc", 1714 | ] 1715 | 1716 | [[package]] 1717 | name = "num_enum" 1718 | version = "0.5.7" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" 1721 | dependencies = [ 1722 | "num_enum_derive", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "num_enum_derive" 1727 | version = "0.5.7" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" 1730 | dependencies = [ 1731 | "proc-macro-crate", 1732 | "proc-macro2", 1733 | "quote", 1734 | "syn", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "objc" 1739 | version = "0.2.7" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 1742 | dependencies = [ 1743 | "malloc_buf", 1744 | "objc_exception", 1745 | ] 1746 | 1747 | [[package]] 1748 | name = "objc_exception" 1749 | version = "0.1.2" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" 1752 | dependencies = [ 1753 | "cc", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "object" 1758 | version = "0.30.3" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" 1761 | dependencies = [ 1762 | "memchr", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "once_cell" 1767 | version = "1.16.0" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 1770 | 1771 | [[package]] 1772 | name = "opaque-debug" 1773 | version = "0.3.0" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1776 | 1777 | [[package]] 1778 | name = "os_str_bytes" 1779 | version = "6.4.1" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 1782 | 1783 | [[package]] 1784 | name = "parking_lot" 1785 | version = "0.12.1" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1788 | dependencies = [ 1789 | "lock_api", 1790 | "parking_lot_core", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "parking_lot_core" 1795 | version = "0.9.5" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" 1798 | dependencies = [ 1799 | "cfg-if", 1800 | "libc", 1801 | "redox_syscall", 1802 | "smallvec", 1803 | "windows-sys 0.42.0", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "percent-encoding" 1808 | version = "2.2.0" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 1811 | 1812 | [[package]] 1813 | name = "pin-project" 1814 | version = "1.0.12" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 1817 | dependencies = [ 1818 | "pin-project-internal", 1819 | ] 1820 | 1821 | [[package]] 1822 | name = "pin-project-internal" 1823 | version = "1.0.12" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 1826 | dependencies = [ 1827 | "proc-macro2", 1828 | "quote", 1829 | "syn", 1830 | ] 1831 | 1832 | [[package]] 1833 | name = "pkg-config" 1834 | version = "0.3.26" 1835 | source = "registry+https://github.com/rust-lang/crates.io-index" 1836 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 1837 | 1838 | [[package]] 1839 | name = "player" 1840 | version = "0.1.0" 1841 | dependencies = [ 1842 | "archive", 1843 | "bytemuck", 1844 | "colors-transform", 1845 | "env_logger", 1846 | "futures-intrusive", 1847 | "game-loop", 1848 | "image", 1849 | "log", 1850 | "num", 1851 | "pollster", 1852 | "rand", 1853 | "ultraviolet", 1854 | "wgpu", 1855 | "winit", 1856 | "winit_input_helper", 1857 | ] 1858 | 1859 | [[package]] 1860 | name = "png" 1861 | version = "0.17.7" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" 1864 | dependencies = [ 1865 | "bitflags", 1866 | "crc32fast", 1867 | "flate2", 1868 | "miniz_oxide 0.6.2", 1869 | ] 1870 | 1871 | [[package]] 1872 | name = "pollster" 1873 | version = "0.2.5" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" 1876 | 1877 | [[package]] 1878 | name = "polyval" 1879 | version = "0.6.0" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" 1882 | dependencies = [ 1883 | "cfg-if", 1884 | "cpufeatures", 1885 | "opaque-debug", 1886 | "universal-hash", 1887 | ] 1888 | 1889 | [[package]] 1890 | name = "ppv-lite86" 1891 | version = "0.2.17" 1892 | source = "registry+https://github.com/rust-lang/crates.io-index" 1893 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1894 | 1895 | [[package]] 1896 | name = "proc-macro-crate" 1897 | version = "1.2.1" 1898 | source = "registry+https://github.com/rust-lang/crates.io-index" 1899 | checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" 1900 | dependencies = [ 1901 | "once_cell", 1902 | "thiserror", 1903 | "toml", 1904 | ] 1905 | 1906 | [[package]] 1907 | name = "proc-macro-error" 1908 | version = "1.0.4" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1911 | dependencies = [ 1912 | "proc-macro-error-attr", 1913 | "proc-macro2", 1914 | "quote", 1915 | "syn", 1916 | "version_check", 1917 | ] 1918 | 1919 | [[package]] 1920 | name = "proc-macro-error-attr" 1921 | version = "1.0.4" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1924 | dependencies = [ 1925 | "proc-macro2", 1926 | "quote", 1927 | "version_check", 1928 | ] 1929 | 1930 | [[package]] 1931 | name = "proc-macro2" 1932 | version = "1.0.60" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 1935 | dependencies = [ 1936 | "unicode-ident", 1937 | ] 1938 | 1939 | [[package]] 1940 | name = "profiling" 1941 | version = "1.0.7" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df" 1944 | 1945 | [[package]] 1946 | name = "quote" 1947 | version = "1.0.21" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 1950 | dependencies = [ 1951 | "proc-macro2", 1952 | ] 1953 | 1954 | [[package]] 1955 | name = "rand" 1956 | version = "0.8.5" 1957 | source = "registry+https://github.com/rust-lang/crates.io-index" 1958 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1959 | dependencies = [ 1960 | "libc", 1961 | "rand_chacha", 1962 | "rand_core 0.6.4", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "rand_chacha" 1967 | version = "0.3.1" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1970 | dependencies = [ 1971 | "ppv-lite86", 1972 | "rand_core 0.6.4", 1973 | ] 1974 | 1975 | [[package]] 1976 | name = "rand_core" 1977 | version = "0.5.1" 1978 | source = "registry+https://github.com/rust-lang/crates.io-index" 1979 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1980 | 1981 | [[package]] 1982 | name = "rand_core" 1983 | version = "0.6.4" 1984 | source = "registry+https://github.com/rust-lang/crates.io-index" 1985 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1986 | dependencies = [ 1987 | "getrandom", 1988 | ] 1989 | 1990 | [[package]] 1991 | name = "range-alloc" 1992 | version = "0.1.2" 1993 | source = "registry+https://github.com/rust-lang/crates.io-index" 1994 | checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" 1995 | 1996 | [[package]] 1997 | name = "raw-window-handle" 1998 | version = "0.4.3" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" 2001 | dependencies = [ 2002 | "cty", 2003 | ] 2004 | 2005 | [[package]] 2006 | name = "raw-window-handle" 2007 | version = "0.5.0" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" 2010 | dependencies = [ 2011 | "cty", 2012 | ] 2013 | 2014 | [[package]] 2015 | name = "rayon" 2016 | version = "1.6.0" 2017 | source = "registry+https://github.com/rust-lang/crates.io-index" 2018 | checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" 2019 | dependencies = [ 2020 | "crossbeam-deque", 2021 | "either", 2022 | "rayon-core", 2023 | ] 2024 | 2025 | [[package]] 2026 | name = "rayon-core" 2027 | version = "1.10.1" 2028 | source = "registry+https://github.com/rust-lang/crates.io-index" 2029 | checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" 2030 | dependencies = [ 2031 | "crossbeam-channel", 2032 | "crossbeam-deque", 2033 | "crossbeam-utils", 2034 | "num_cpus", 2035 | ] 2036 | 2037 | [[package]] 2038 | name = "redox_syscall" 2039 | version = "0.2.16" 2040 | source = "registry+https://github.com/rust-lang/crates.io-index" 2041 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 2042 | dependencies = [ 2043 | "bitflags", 2044 | ] 2045 | 2046 | [[package]] 2047 | name = "regex" 2048 | version = "1.7.1" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 2051 | dependencies = [ 2052 | "aho-corasick", 2053 | "memchr", 2054 | "regex-syntax", 2055 | ] 2056 | 2057 | [[package]] 2058 | name = "regex-automata" 2059 | version = "0.1.10" 2060 | source = "registry+https://github.com/rust-lang/crates.io-index" 2061 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 2062 | 2063 | [[package]] 2064 | name = "regex-syntax" 2065 | version = "0.6.28" 2066 | source = "registry+https://github.com/rust-lang/crates.io-index" 2067 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 2068 | 2069 | [[package]] 2070 | name = "remove_dir_all" 2071 | version = "0.5.3" 2072 | source = "registry+https://github.com/rust-lang/crates.io-index" 2073 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 2074 | dependencies = [ 2075 | "winapi", 2076 | ] 2077 | 2078 | [[package]] 2079 | name = "renderdoc-sys" 2080 | version = "0.7.1" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" 2083 | 2084 | [[package]] 2085 | name = "rustc-demangle" 2086 | version = "0.1.21" 2087 | source = "registry+https://github.com/rust-lang/crates.io-index" 2088 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 2089 | 2090 | [[package]] 2091 | name = "rustc-hash" 2092 | version = "1.1.0" 2093 | source = "registry+https://github.com/rust-lang/crates.io-index" 2094 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 2095 | 2096 | [[package]] 2097 | name = "rustix" 2098 | version = "0.36.5" 2099 | source = "registry+https://github.com/rust-lang/crates.io-index" 2100 | checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" 2101 | dependencies = [ 2102 | "bitflags", 2103 | "errno", 2104 | "io-lifetimes", 2105 | "libc", 2106 | "linux-raw-sys", 2107 | "windows-sys 0.42.0", 2108 | ] 2109 | 2110 | [[package]] 2111 | name = "ryu" 2112 | version = "1.0.11" 2113 | source = "registry+https://github.com/rust-lang/crates.io-index" 2114 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 2115 | 2116 | [[package]] 2117 | name = "safe_arch" 2118 | version = "0.5.2" 2119 | source = "registry+https://github.com/rust-lang/crates.io-index" 2120 | checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" 2121 | dependencies = [ 2122 | "bytemuck", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "safe_arch" 2127 | version = "0.6.0" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" 2130 | dependencies = [ 2131 | "bytemuck", 2132 | ] 2133 | 2134 | [[package]] 2135 | name = "scoped-tls" 2136 | version = "1.0.1" 2137 | source = "registry+https://github.com/rust-lang/crates.io-index" 2138 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 2139 | 2140 | [[package]] 2141 | name = "scoped_threadpool" 2142 | version = "0.1.9" 2143 | source = "registry+https://github.com/rust-lang/crates.io-index" 2144 | checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 2145 | 2146 | [[package]] 2147 | name = "scopeguard" 2148 | version = "1.1.0" 2149 | source = "registry+https://github.com/rust-lang/crates.io-index" 2150 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 2151 | 2152 | [[package]] 2153 | name = "scratch" 2154 | version = "1.0.2" 2155 | source = "registry+https://github.com/rust-lang/crates.io-index" 2156 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" 2157 | 2158 | [[package]] 2159 | name = "sctk-adwaita" 2160 | version = "0.4.3" 2161 | source = "registry+https://github.com/rust-lang/crates.io-index" 2162 | checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" 2163 | dependencies = [ 2164 | "crossfont", 2165 | "log", 2166 | "smithay-client-toolkit", 2167 | "tiny-skia", 2168 | ] 2169 | 2170 | [[package]] 2171 | name = "serde" 2172 | version = "1.0.147" 2173 | source = "registry+https://github.com/rust-lang/crates.io-index" 2174 | checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" 2175 | dependencies = [ 2176 | "serde_derive", 2177 | ] 2178 | 2179 | [[package]] 2180 | name = "serde_derive" 2181 | version = "1.0.147" 2182 | source = "registry+https://github.com/rust-lang/crates.io-index" 2183 | checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" 2184 | dependencies = [ 2185 | "proc-macro2", 2186 | "quote", 2187 | "syn", 2188 | ] 2189 | 2190 | [[package]] 2191 | name = "servo-fontconfig" 2192 | version = "0.5.1" 2193 | source = "registry+https://github.com/rust-lang/crates.io-index" 2194 | checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" 2195 | dependencies = [ 2196 | "libc", 2197 | "servo-fontconfig-sys", 2198 | ] 2199 | 2200 | [[package]] 2201 | name = "servo-fontconfig-sys" 2202 | version = "5.1.0" 2203 | source = "registry+https://github.com/rust-lang/crates.io-index" 2204 | checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" 2205 | dependencies = [ 2206 | "expat-sys", 2207 | "freetype-sys", 2208 | "pkg-config", 2209 | ] 2210 | 2211 | [[package]] 2212 | name = "sha2" 2213 | version = "0.10.6" 2214 | source = "registry+https://github.com/rust-lang/crates.io-index" 2215 | checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" 2216 | dependencies = [ 2217 | "cfg-if", 2218 | "cpufeatures", 2219 | "digest 0.10.6", 2220 | ] 2221 | 2222 | [[package]] 2223 | name = "slotmap" 2224 | version = "1.0.6" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" 2227 | dependencies = [ 2228 | "version_check", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "smallvec" 2233 | version = "1.10.0" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 2236 | 2237 | [[package]] 2238 | name = "smithay-client-toolkit" 2239 | version = "0.16.0" 2240 | source = "registry+https://github.com/rust-lang/crates.io-index" 2241 | checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" 2242 | dependencies = [ 2243 | "bitflags", 2244 | "calloop", 2245 | "dlib", 2246 | "lazy_static", 2247 | "log", 2248 | "memmap2", 2249 | "nix 0.24.3", 2250 | "pkg-config", 2251 | "wayland-client", 2252 | "wayland-cursor", 2253 | "wayland-protocols", 2254 | ] 2255 | 2256 | [[package]] 2257 | name = "spin" 2258 | version = "0.9.4" 2259 | source = "registry+https://github.com/rust-lang/crates.io-index" 2260 | checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" 2261 | dependencies = [ 2262 | "lock_api", 2263 | ] 2264 | 2265 | [[package]] 2266 | name = "spirv" 2267 | version = "0.2.0+1.5.4" 2268 | source = "registry+https://github.com/rust-lang/crates.io-index" 2269 | checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" 2270 | dependencies = [ 2271 | "bitflags", 2272 | "num-traits", 2273 | ] 2274 | 2275 | [[package]] 2276 | name = "static_assertions" 2277 | version = "1.1.0" 2278 | source = "registry+https://github.com/rust-lang/crates.io-index" 2279 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 2280 | 2281 | [[package]] 2282 | name = "strsim" 2283 | version = "0.10.0" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 2286 | 2287 | [[package]] 2288 | name = "subtle" 2289 | version = "2.4.1" 2290 | source = "registry+https://github.com/rust-lang/crates.io-index" 2291 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 2292 | 2293 | [[package]] 2294 | name = "syn" 2295 | version = "1.0.103" 2296 | source = "registry+https://github.com/rust-lang/crates.io-index" 2297 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 2298 | dependencies = [ 2299 | "proc-macro2", 2300 | "quote", 2301 | "unicode-ident", 2302 | ] 2303 | 2304 | [[package]] 2305 | name = "synstructure" 2306 | version = "0.12.6" 2307 | source = "registry+https://github.com/rust-lang/crates.io-index" 2308 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 2309 | dependencies = [ 2310 | "proc-macro2", 2311 | "quote", 2312 | "syn", 2313 | "unicode-xid", 2314 | ] 2315 | 2316 | [[package]] 2317 | name = "tempfile" 2318 | version = "3.3.0" 2319 | source = "registry+https://github.com/rust-lang/crates.io-index" 2320 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 2321 | dependencies = [ 2322 | "cfg-if", 2323 | "fastrand", 2324 | "libc", 2325 | "redox_syscall", 2326 | "remove_dir_all", 2327 | "winapi", 2328 | ] 2329 | 2330 | [[package]] 2331 | name = "termcolor" 2332 | version = "1.1.3" 2333 | source = "registry+https://github.com/rust-lang/crates.io-index" 2334 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 2335 | dependencies = [ 2336 | "winapi-util", 2337 | ] 2338 | 2339 | [[package]] 2340 | name = "thiserror" 2341 | version = "1.0.38" 2342 | source = "registry+https://github.com/rust-lang/crates.io-index" 2343 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 2344 | dependencies = [ 2345 | "thiserror-impl", 2346 | ] 2347 | 2348 | [[package]] 2349 | name = "thiserror-impl" 2350 | version = "1.0.38" 2351 | source = "registry+https://github.com/rust-lang/crates.io-index" 2352 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 2353 | dependencies = [ 2354 | "proc-macro2", 2355 | "quote", 2356 | "syn", 2357 | ] 2358 | 2359 | [[package]] 2360 | name = "threadpool" 2361 | version = "1.8.1" 2362 | source = "registry+https://github.com/rust-lang/crates.io-index" 2363 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 2364 | dependencies = [ 2365 | "num_cpus", 2366 | ] 2367 | 2368 | [[package]] 2369 | name = "tiff" 2370 | version = "0.8.0" 2371 | source = "registry+https://github.com/rust-lang/crates.io-index" 2372 | checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd" 2373 | dependencies = [ 2374 | "flate2", 2375 | "jpeg-decoder", 2376 | "weezl", 2377 | ] 2378 | 2379 | [[package]] 2380 | name = "time" 2381 | version = "0.1.44" 2382 | source = "registry+https://github.com/rust-lang/crates.io-index" 2383 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 2384 | dependencies = [ 2385 | "libc", 2386 | "wasi 0.10.0+wasi-snapshot-preview1", 2387 | "winapi", 2388 | ] 2389 | 2390 | [[package]] 2391 | name = "tiny-skia" 2392 | version = "0.7.0" 2393 | source = "registry+https://github.com/rust-lang/crates.io-index" 2394 | checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" 2395 | dependencies = [ 2396 | "arrayref", 2397 | "arrayvec 0.5.2", 2398 | "bytemuck", 2399 | "cfg-if", 2400 | "png", 2401 | "safe_arch 0.5.2", 2402 | "tiny-skia-path", 2403 | ] 2404 | 2405 | [[package]] 2406 | name = "tiny-skia-path" 2407 | version = "0.7.0" 2408 | source = "registry+https://github.com/rust-lang/crates.io-index" 2409 | checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" 2410 | dependencies = [ 2411 | "arrayref", 2412 | "bytemuck", 2413 | ] 2414 | 2415 | [[package]] 2416 | name = "toml" 2417 | version = "0.5.10" 2418 | source = "registry+https://github.com/rust-lang/crates.io-index" 2419 | checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" 2420 | dependencies = [ 2421 | "serde", 2422 | ] 2423 | 2424 | [[package]] 2425 | name = "typenum" 2426 | version = "1.16.0" 2427 | source = "registry+https://github.com/rust-lang/crates.io-index" 2428 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 2429 | 2430 | [[package]] 2431 | name = "ultraviolet" 2432 | version = "0.9.0" 2433 | source = "registry+https://github.com/rust-lang/crates.io-index" 2434 | checksum = "bf2728f3858937f50df6d92b741f53ef8fd5b5ae23b03b73fb9ac891e980df3b" 2435 | dependencies = [ 2436 | "wide", 2437 | ] 2438 | 2439 | [[package]] 2440 | name = "unicode-ident" 2441 | version = "1.0.5" 2442 | source = "registry+https://github.com/rust-lang/crates.io-index" 2443 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 2444 | 2445 | [[package]] 2446 | name = "unicode-width" 2447 | version = "0.1.10" 2448 | source = "registry+https://github.com/rust-lang/crates.io-index" 2449 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 2450 | 2451 | [[package]] 2452 | name = "unicode-xid" 2453 | version = "0.2.4" 2454 | source = "registry+https://github.com/rust-lang/crates.io-index" 2455 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 2456 | 2457 | [[package]] 2458 | name = "universal-hash" 2459 | version = "0.5.0" 2460 | source = "registry+https://github.com/rust-lang/crates.io-index" 2461 | checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" 2462 | dependencies = [ 2463 | "crypto-common", 2464 | "subtle", 2465 | ] 2466 | 2467 | [[package]] 2468 | name = "utf8-width" 2469 | version = "0.1.6" 2470 | source = "registry+https://github.com/rust-lang/crates.io-index" 2471 | checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" 2472 | 2473 | [[package]] 2474 | name = "vec_map" 2475 | version = "0.8.2" 2476 | source = "registry+https://github.com/rust-lang/crates.io-index" 2477 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 2478 | 2479 | [[package]] 2480 | name = "version_check" 2481 | version = "0.9.4" 2482 | source = "registry+https://github.com/rust-lang/crates.io-index" 2483 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2484 | 2485 | [[package]] 2486 | name = "virtue" 2487 | version = "0.0.8" 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" 2489 | checksum = "7b60dcd6a64dd45abf9bd426970c9843726da7fc08f44cd6fcebf68c21220a63" 2490 | 2491 | [[package]] 2492 | name = "wasi" 2493 | version = "0.10.0+wasi-snapshot-preview1" 2494 | source = "registry+https://github.com/rust-lang/crates.io-index" 2495 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 2496 | 2497 | [[package]] 2498 | name = "wasi" 2499 | version = "0.11.0+wasi-snapshot-preview1" 2500 | source = "registry+https://github.com/rust-lang/crates.io-index" 2501 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2502 | 2503 | [[package]] 2504 | name = "wasm-bindgen" 2505 | version = "0.2.83" 2506 | source = "registry+https://github.com/rust-lang/crates.io-index" 2507 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 2508 | dependencies = [ 2509 | "cfg-if", 2510 | "wasm-bindgen-macro", 2511 | ] 2512 | 2513 | [[package]] 2514 | name = "wasm-bindgen-backend" 2515 | version = "0.2.83" 2516 | source = "registry+https://github.com/rust-lang/crates.io-index" 2517 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 2518 | dependencies = [ 2519 | "bumpalo", 2520 | "log", 2521 | "once_cell", 2522 | "proc-macro2", 2523 | "quote", 2524 | "syn", 2525 | "wasm-bindgen-shared", 2526 | ] 2527 | 2528 | [[package]] 2529 | name = "wasm-bindgen-futures" 2530 | version = "0.4.33" 2531 | source = "registry+https://github.com/rust-lang/crates.io-index" 2532 | checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" 2533 | dependencies = [ 2534 | "cfg-if", 2535 | "js-sys", 2536 | "wasm-bindgen", 2537 | "web-sys", 2538 | ] 2539 | 2540 | [[package]] 2541 | name = "wasm-bindgen-macro" 2542 | version = "0.2.83" 2543 | source = "registry+https://github.com/rust-lang/crates.io-index" 2544 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 2545 | dependencies = [ 2546 | "quote", 2547 | "wasm-bindgen-macro-support", 2548 | ] 2549 | 2550 | [[package]] 2551 | name = "wasm-bindgen-macro-support" 2552 | version = "0.2.83" 2553 | source = "registry+https://github.com/rust-lang/crates.io-index" 2554 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 2555 | dependencies = [ 2556 | "proc-macro2", 2557 | "quote", 2558 | "syn", 2559 | "wasm-bindgen-backend", 2560 | "wasm-bindgen-shared", 2561 | ] 2562 | 2563 | [[package]] 2564 | name = "wasm-bindgen-shared" 2565 | version = "0.2.83" 2566 | source = "registry+https://github.com/rust-lang/crates.io-index" 2567 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 2568 | 2569 | [[package]] 2570 | name = "wayland-client" 2571 | version = "0.29.5" 2572 | source = "registry+https://github.com/rust-lang/crates.io-index" 2573 | checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" 2574 | dependencies = [ 2575 | "bitflags", 2576 | "downcast-rs", 2577 | "libc", 2578 | "nix 0.24.3", 2579 | "scoped-tls", 2580 | "wayland-commons", 2581 | "wayland-scanner", 2582 | "wayland-sys", 2583 | ] 2584 | 2585 | [[package]] 2586 | name = "wayland-commons" 2587 | version = "0.29.5" 2588 | source = "registry+https://github.com/rust-lang/crates.io-index" 2589 | checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" 2590 | dependencies = [ 2591 | "nix 0.24.3", 2592 | "once_cell", 2593 | "smallvec", 2594 | "wayland-sys", 2595 | ] 2596 | 2597 | [[package]] 2598 | name = "wayland-cursor" 2599 | version = "0.29.5" 2600 | source = "registry+https://github.com/rust-lang/crates.io-index" 2601 | checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" 2602 | dependencies = [ 2603 | "nix 0.24.3", 2604 | "wayland-client", 2605 | "xcursor", 2606 | ] 2607 | 2608 | [[package]] 2609 | name = "wayland-protocols" 2610 | version = "0.29.5" 2611 | source = "registry+https://github.com/rust-lang/crates.io-index" 2612 | checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" 2613 | dependencies = [ 2614 | "bitflags", 2615 | "wayland-client", 2616 | "wayland-commons", 2617 | "wayland-scanner", 2618 | ] 2619 | 2620 | [[package]] 2621 | name = "wayland-scanner" 2622 | version = "0.29.5" 2623 | source = "registry+https://github.com/rust-lang/crates.io-index" 2624 | checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" 2625 | dependencies = [ 2626 | "proc-macro2", 2627 | "quote", 2628 | "xml-rs", 2629 | ] 2630 | 2631 | [[package]] 2632 | name = "wayland-sys" 2633 | version = "0.29.5" 2634 | source = "registry+https://github.com/rust-lang/crates.io-index" 2635 | checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" 2636 | dependencies = [ 2637 | "dlib", 2638 | "lazy_static", 2639 | "pkg-config", 2640 | ] 2641 | 2642 | [[package]] 2643 | name = "web-sys" 2644 | version = "0.3.60" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" 2647 | dependencies = [ 2648 | "js-sys", 2649 | "wasm-bindgen", 2650 | ] 2651 | 2652 | [[package]] 2653 | name = "weezl" 2654 | version = "0.1.7" 2655 | source = "registry+https://github.com/rust-lang/crates.io-index" 2656 | checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" 2657 | 2658 | [[package]] 2659 | name = "wgpu" 2660 | version = "0.15.0" 2661 | source = "registry+https://github.com/rust-lang/crates.io-index" 2662 | checksum = "d14c6bfcf3b10f4273f522a95994553c0a5f2934976e62e61a720ae4bc2eb8f2" 2663 | dependencies = [ 2664 | "arrayvec 0.7.2", 2665 | "cfg-if", 2666 | "js-sys", 2667 | "log", 2668 | "naga", 2669 | "parking_lot", 2670 | "profiling", 2671 | "raw-window-handle 0.5.0", 2672 | "smallvec", 2673 | "static_assertions", 2674 | "wasm-bindgen", 2675 | "wasm-bindgen-futures", 2676 | "web-sys", 2677 | "wgpu-core", 2678 | "wgpu-hal", 2679 | "wgpu-types", 2680 | ] 2681 | 2682 | [[package]] 2683 | name = "wgpu-core" 2684 | version = "0.15.0" 2685 | source = "registry+https://github.com/rust-lang/crates.io-index" 2686 | checksum = "be1f61be28e557a6ecb2506cac06c63fae3b6d302a006f38195a7a80995abeb9" 2687 | dependencies = [ 2688 | "arrayvec 0.7.2", 2689 | "bit-vec", 2690 | "bitflags", 2691 | "codespan-reporting", 2692 | "fxhash", 2693 | "log", 2694 | "naga", 2695 | "parking_lot", 2696 | "profiling", 2697 | "raw-window-handle 0.5.0", 2698 | "smallvec", 2699 | "thiserror", 2700 | "web-sys", 2701 | "wgpu-hal", 2702 | "wgpu-types", 2703 | ] 2704 | 2705 | [[package]] 2706 | name = "wgpu-hal" 2707 | version = "0.15.1" 2708 | source = "registry+https://github.com/rust-lang/crates.io-index" 2709 | checksum = "82e95792925fe3d58950b9a5c2a191caa145e2bc570e2d233f0d7320f6a8e814" 2710 | dependencies = [ 2711 | "android_system_properties", 2712 | "arrayvec 0.7.2", 2713 | "ash", 2714 | "bit-set", 2715 | "bitflags", 2716 | "block", 2717 | "core-graphics-types", 2718 | "d3d12", 2719 | "foreign-types 0.3.2", 2720 | "fxhash", 2721 | "glow", 2722 | "gpu-alloc", 2723 | "gpu-allocator", 2724 | "gpu-descriptor", 2725 | "hassle-rs", 2726 | "js-sys", 2727 | "khronos-egl", 2728 | "libc", 2729 | "libloading", 2730 | "log", 2731 | "metal", 2732 | "naga", 2733 | "objc", 2734 | "parking_lot", 2735 | "profiling", 2736 | "range-alloc", 2737 | "raw-window-handle 0.5.0", 2738 | "renderdoc-sys", 2739 | "smallvec", 2740 | "thiserror", 2741 | "wasm-bindgen", 2742 | "web-sys", 2743 | "wgpu-types", 2744 | "winapi", 2745 | ] 2746 | 2747 | [[package]] 2748 | name = "wgpu-types" 2749 | version = "0.15.0" 2750 | source = "registry+https://github.com/rust-lang/crates.io-index" 2751 | checksum = "ecf8cfcbf98f94cc8bd5981544c687140cf9d3948e2ab83849367ead2cd737cf" 2752 | dependencies = [ 2753 | "bitflags", 2754 | "js-sys", 2755 | "web-sys", 2756 | ] 2757 | 2758 | [[package]] 2759 | name = "wide" 2760 | version = "0.7.5" 2761 | source = "registry+https://github.com/rust-lang/crates.io-index" 2762 | checksum = "ae41ecad2489a1655c8ef8489444b0b113c0a0c795944a3572a0931cf7d2525c" 2763 | dependencies = [ 2764 | "bytemuck", 2765 | "safe_arch 0.6.0", 2766 | ] 2767 | 2768 | [[package]] 2769 | name = "widestring" 2770 | version = "0.5.1" 2771 | source = "registry+https://github.com/rust-lang/crates.io-index" 2772 | checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" 2773 | 2774 | [[package]] 2775 | name = "winapi" 2776 | version = "0.3.9" 2777 | source = "registry+https://github.com/rust-lang/crates.io-index" 2778 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2779 | dependencies = [ 2780 | "winapi-i686-pc-windows-gnu", 2781 | "winapi-x86_64-pc-windows-gnu", 2782 | ] 2783 | 2784 | [[package]] 2785 | name = "winapi-i686-pc-windows-gnu" 2786 | version = "0.4.0" 2787 | source = "registry+https://github.com/rust-lang/crates.io-index" 2788 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2789 | 2790 | [[package]] 2791 | name = "winapi-util" 2792 | version = "0.1.5" 2793 | source = "registry+https://github.com/rust-lang/crates.io-index" 2794 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2795 | dependencies = [ 2796 | "winapi", 2797 | ] 2798 | 2799 | [[package]] 2800 | name = "winapi-x86_64-pc-windows-gnu" 2801 | version = "0.4.0" 2802 | source = "registry+https://github.com/rust-lang/crates.io-index" 2803 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2804 | 2805 | [[package]] 2806 | name = "windows" 2807 | version = "0.43.0" 2808 | source = "registry+https://github.com/rust-lang/crates.io-index" 2809 | checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" 2810 | dependencies = [ 2811 | "windows_aarch64_gnullvm", 2812 | "windows_aarch64_msvc 0.42.0", 2813 | "windows_i686_gnu 0.42.0", 2814 | "windows_i686_msvc 0.42.0", 2815 | "windows_x86_64_gnu 0.42.0", 2816 | "windows_x86_64_gnullvm", 2817 | "windows_x86_64_msvc 0.42.0", 2818 | ] 2819 | 2820 | [[package]] 2821 | name = "windows-sys" 2822 | version = "0.36.1" 2823 | source = "registry+https://github.com/rust-lang/crates.io-index" 2824 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 2825 | dependencies = [ 2826 | "windows_aarch64_msvc 0.36.1", 2827 | "windows_i686_gnu 0.36.1", 2828 | "windows_i686_msvc 0.36.1", 2829 | "windows_x86_64_gnu 0.36.1", 2830 | "windows_x86_64_msvc 0.36.1", 2831 | ] 2832 | 2833 | [[package]] 2834 | name = "windows-sys" 2835 | version = "0.42.0" 2836 | source = "registry+https://github.com/rust-lang/crates.io-index" 2837 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 2838 | dependencies = [ 2839 | "windows_aarch64_gnullvm", 2840 | "windows_aarch64_msvc 0.42.0", 2841 | "windows_i686_gnu 0.42.0", 2842 | "windows_i686_msvc 0.42.0", 2843 | "windows_x86_64_gnu 0.42.0", 2844 | "windows_x86_64_gnullvm", 2845 | "windows_x86_64_msvc 0.42.0", 2846 | ] 2847 | 2848 | [[package]] 2849 | name = "windows_aarch64_gnullvm" 2850 | version = "0.42.0" 2851 | source = "registry+https://github.com/rust-lang/crates.io-index" 2852 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 2853 | 2854 | [[package]] 2855 | name = "windows_aarch64_msvc" 2856 | version = "0.36.1" 2857 | source = "registry+https://github.com/rust-lang/crates.io-index" 2858 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 2859 | 2860 | [[package]] 2861 | name = "windows_aarch64_msvc" 2862 | version = "0.42.0" 2863 | source = "registry+https://github.com/rust-lang/crates.io-index" 2864 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 2865 | 2866 | [[package]] 2867 | name = "windows_i686_gnu" 2868 | version = "0.36.1" 2869 | source = "registry+https://github.com/rust-lang/crates.io-index" 2870 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 2871 | 2872 | [[package]] 2873 | name = "windows_i686_gnu" 2874 | version = "0.42.0" 2875 | source = "registry+https://github.com/rust-lang/crates.io-index" 2876 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 2877 | 2878 | [[package]] 2879 | name = "windows_i686_msvc" 2880 | version = "0.36.1" 2881 | source = "registry+https://github.com/rust-lang/crates.io-index" 2882 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 2883 | 2884 | [[package]] 2885 | name = "windows_i686_msvc" 2886 | version = "0.42.0" 2887 | source = "registry+https://github.com/rust-lang/crates.io-index" 2888 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 2889 | 2890 | [[package]] 2891 | name = "windows_x86_64_gnu" 2892 | version = "0.36.1" 2893 | source = "registry+https://github.com/rust-lang/crates.io-index" 2894 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 2895 | 2896 | [[package]] 2897 | name = "windows_x86_64_gnu" 2898 | version = "0.42.0" 2899 | source = "registry+https://github.com/rust-lang/crates.io-index" 2900 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 2901 | 2902 | [[package]] 2903 | name = "windows_x86_64_gnullvm" 2904 | version = "0.42.0" 2905 | source = "registry+https://github.com/rust-lang/crates.io-index" 2906 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 2907 | 2908 | [[package]] 2909 | name = "windows_x86_64_msvc" 2910 | version = "0.36.1" 2911 | source = "registry+https://github.com/rust-lang/crates.io-index" 2912 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 2913 | 2914 | [[package]] 2915 | name = "windows_x86_64_msvc" 2916 | version = "0.42.0" 2917 | source = "registry+https://github.com/rust-lang/crates.io-index" 2918 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 2919 | 2920 | [[package]] 2921 | name = "winit" 2922 | version = "0.27.5" 2923 | source = "registry+https://github.com/rust-lang/crates.io-index" 2924 | checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" 2925 | dependencies = [ 2926 | "bitflags", 2927 | "cocoa", 2928 | "core-foundation", 2929 | "core-graphics", 2930 | "dispatch", 2931 | "instant", 2932 | "libc", 2933 | "log", 2934 | "mio", 2935 | "ndk", 2936 | "ndk-glue", 2937 | "objc", 2938 | "once_cell", 2939 | "parking_lot", 2940 | "percent-encoding", 2941 | "raw-window-handle 0.4.3", 2942 | "raw-window-handle 0.5.0", 2943 | "sctk-adwaita", 2944 | "smithay-client-toolkit", 2945 | "wasm-bindgen", 2946 | "wayland-client", 2947 | "wayland-protocols", 2948 | "web-sys", 2949 | "windows-sys 0.36.1", 2950 | "x11-dl", 2951 | ] 2952 | 2953 | [[package]] 2954 | name = "winit_input_helper" 2955 | version = "0.13.0" 2956 | source = "registry+https://github.com/rust-lang/crates.io-index" 2957 | checksum = "de23d6018b4ff6e9a6bd069109b3c891a44acc97a212e744598c6971a9ee0384" 2958 | dependencies = [ 2959 | "winit", 2960 | ] 2961 | 2962 | [[package]] 2963 | name = "wio" 2964 | version = "0.2.2" 2965 | source = "registry+https://github.com/rust-lang/crates.io-index" 2966 | checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" 2967 | dependencies = [ 2968 | "winapi", 2969 | ] 2970 | 2971 | [[package]] 2972 | name = "x11-dl" 2973 | version = "2.20.1" 2974 | source = "registry+https://github.com/rust-lang/crates.io-index" 2975 | checksum = "b1536d6965a5d4e573c7ef73a2c15ebcd0b2de3347bdf526c34c297c00ac40f0" 2976 | dependencies = [ 2977 | "lazy_static", 2978 | "libc", 2979 | "pkg-config", 2980 | ] 2981 | 2982 | [[package]] 2983 | name = "x25519-dalek" 2984 | version = "1.2.0" 2985 | source = "registry+https://github.com/rust-lang/crates.io-index" 2986 | checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077" 2987 | dependencies = [ 2988 | "curve25519-dalek", 2989 | "rand_core 0.5.1", 2990 | "zeroize", 2991 | ] 2992 | 2993 | [[package]] 2994 | name = "xcursor" 2995 | version = "0.3.4" 2996 | source = "registry+https://github.com/rust-lang/crates.io-index" 2997 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 2998 | dependencies = [ 2999 | "nom", 3000 | ] 3001 | 3002 | [[package]] 3003 | name = "xml-rs" 3004 | version = "0.8.4" 3005 | source = "registry+https://github.com/rust-lang/crates.io-index" 3006 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 3007 | 3008 | [[package]] 3009 | name = "zeroize" 3010 | version = "1.3.0" 3011 | source = "registry+https://github.com/rust-lang/crates.io-index" 3012 | checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" 3013 | dependencies = [ 3014 | "zeroize_derive", 3015 | ] 3016 | 3017 | [[package]] 3018 | name = "zeroize_derive" 3019 | version = "1.3.3" 3020 | source = "registry+https://github.com/rust-lang/crates.io-index" 3021 | checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" 3022 | dependencies = [ 3023 | "proc-macro2", 3024 | "quote", 3025 | "syn", 3026 | "synstructure", 3027 | ] 3028 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["archive", "cli", "player"] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # placed 2 | 3 | Still heavily a work-in-progress. Don't expect anything to work. 4 | -------------------------------------------------------------------------------- /archive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "archive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | image = "0.24.5" 8 | chrono = "0.4.23" 9 | tempfile = "3.3.0" 10 | bincode = "2.0.0-rc.1" 11 | mla = "1.3.0" 12 | colors-transform = "0.2.11" 13 | 14 | [dev-dependencies] 15 | rand = "0.8.5" 16 | -------------------------------------------------------------------------------- /archive/src/archive_reader.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Cursor, Read, Seek}; 2 | 3 | use mla::ArchiveReader; 4 | 5 | use crate::{ 6 | constants::BINCODE_CONFIG, 7 | errors::{NextTileChunkError, PlacedArchiveError}, 8 | structures::{DecodedTilePlacement, Meta, StoredTilePlacement}, 9 | }; 10 | 11 | pub struct PlacedArchiveReader<'a, R: Read + Seek> { 12 | mla: ArchiveReader<'a, R>, 13 | pub meta: Meta, 14 | current_tile_chunk_id: Option, 15 | current_tile_chunk_data: Option>>, 16 | } 17 | 18 | impl<'a, R: Read + Seek + 'a> PlacedArchiveReader<'a, R> { 19 | pub fn new(reader: R) -> Result { 20 | let mut mla = match ArchiveReader::new(reader) { 21 | Ok(mla) => mla, 22 | Err(err) => return Err(PlacedArchiveError::MLAReadError(err)), 23 | }; 24 | 25 | let mut meta_file = match mla.get_file("meta".to_string()) { 26 | Ok(Some(meta_file)) => meta_file, 27 | Ok(None) => return Err(PlacedArchiveError::MissingMetaFile), 28 | Err(_) => return Err(PlacedArchiveError::MissingMetaFile), 29 | }; 30 | 31 | let meta: Meta = match bincode::decode_from_std_read(&mut meta_file.data, BINCODE_CONFIG) { 32 | Ok(meta) => meta, 33 | Err(_) => return Err(PlacedArchiveError::CouldNotDecodeMetaFile), 34 | }; 35 | 36 | Ok(Self { 37 | mla, 38 | meta, 39 | current_tile_chunk_id: None, 40 | current_tile_chunk_data: None, 41 | }) 42 | } 43 | 44 | fn load_chunk_by_id(&mut self, tile_chunk_id: u32) -> Result<(), NextTileChunkError> { 45 | let tile_chunk_file_name = format!("tiles/{}", tile_chunk_id); 46 | 47 | let mut current_tile_chunk_file = match self.mla.get_file(tile_chunk_file_name) { 48 | Ok(Some(tile_chunk_file)) => tile_chunk_file, 49 | Ok(None) => return Err(NextTileChunkError::MissingChunkFile), 50 | Err(err) => return Err(NextTileChunkError::CouldNotFetchChunkFile(err)), 51 | }; 52 | 53 | let mut buf = Vec::with_capacity(current_tile_chunk_file.size as usize); 54 | std::io::copy(&mut current_tile_chunk_file.data, &mut buf).unwrap(); 55 | self.current_tile_chunk_data = Some(Cursor::new(buf)); 56 | 57 | Ok(()) 58 | } 59 | 60 | fn get_next_chunk_data(&mut self) -> Result<(), NextTileChunkError> { 61 | let tile_chunk_id = match self.current_tile_chunk_id { 62 | Some(id) => id + 1, 63 | None => 0, 64 | }; 65 | 66 | if tile_chunk_id >= self.meta.chunk_descs.len() as u32 { 67 | return Err(NextTileChunkError::OutOfChunks); 68 | } 69 | 70 | match self.load_chunk_by_id(tile_chunk_id) { 71 | Ok(_) => { 72 | self.current_tile_chunk_id = Some(tile_chunk_id); 73 | Ok(()) 74 | } 75 | Err(err) => return Err(err), 76 | } 77 | } 78 | } 79 | 80 | impl<'a, R: Read + Seek> Read for PlacedArchiveReader<'a, R> { 81 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 82 | match &mut self.current_tile_chunk_data { 83 | Some(ref mut data) => { 84 | if data.position() == data.get_ref().len() as u64 { 85 | match self.get_next_chunk_data() { 86 | Ok(_) => self.read(buf), 87 | Err(_) => Ok(0), 88 | } 89 | } else { 90 | data.read(buf) 91 | } 92 | } 93 | None => match self.get_next_chunk_data() { 94 | Ok(_) => self.read(buf), 95 | Err(_) => Ok(0), 96 | }, 97 | } 98 | } 99 | } 100 | 101 | impl<'a, R: Read + Seek> Seek for PlacedArchiveReader<'a, R> { 102 | fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { 103 | match pos { 104 | std::io::SeekFrom::Start(pos) => { 105 | let mut new_current_tile_chunk_id = 0; 106 | let mut current_tile_offset = 0; 107 | 108 | let pos_in_tiles = pos / StoredTilePlacement::encoded_size() as u64; 109 | 110 | for chunk_desc in &self.meta.chunk_descs { 111 | if current_tile_offset + chunk_desc.num_tiles as u64 >= pos_in_tiles { 112 | break; 113 | } 114 | 115 | new_current_tile_chunk_id += 1; 116 | current_tile_offset += chunk_desc.num_tiles as u64; 117 | } 118 | 119 | if self.current_tile_chunk_id != Some(new_current_tile_chunk_id) 120 | || self.current_tile_chunk_data.is_none() 121 | { 122 | match self.load_chunk_by_id(new_current_tile_chunk_id) { 123 | Ok(_) => { 124 | self.current_tile_chunk_id = Some(new_current_tile_chunk_id); 125 | } 126 | Err(_) => { 127 | return Err(std::io::Error::new( 128 | std::io::ErrorKind::Other, 129 | "Could not load chunk", 130 | )); 131 | } 132 | }; 133 | } 134 | 135 | let remaining_pos = pos % StoredTilePlacement::encoded_size() as u64 136 | + (pos_in_tiles - current_tile_offset) 137 | * StoredTilePlacement::encoded_size() as u64; 138 | 139 | match self 140 | .current_tile_chunk_data 141 | .as_mut() 142 | .unwrap() 143 | .seek(std::io::SeekFrom::Start(remaining_pos)) 144 | { 145 | Ok(_) => Ok(pos), 146 | Err(_) => Err(std::io::Error::new( 147 | std::io::ErrorKind::Other, 148 | "Could not seek within chunk", 149 | )), 150 | } 151 | } 152 | std::io::SeekFrom::Current(pos) => { 153 | let current_byte_offset_within_chunk = match &self.current_tile_chunk_data { 154 | Some(data) => data.position(), 155 | None => 0, 156 | }; 157 | 158 | let base_byte_offset = self 159 | .meta 160 | .chunk_descs 161 | .iter() 162 | .take(self.current_tile_chunk_id.unwrap() as usize) 163 | .fold(0, |acc, desc| { 164 | acc + desc.num_tiles as u64 * StoredTilePlacement::encoded_size() as u64 165 | }); 166 | 167 | self.seek(std::io::SeekFrom::Start( 168 | (base_byte_offset as i64 + current_byte_offset_within_chunk as i64 + pos) 169 | as u64, 170 | )) 171 | } 172 | std::io::SeekFrom::End(pos) => { 173 | let total_num_of_tiles = self 174 | .meta 175 | .chunk_descs 176 | .iter() 177 | .fold(0, |acc, desc| acc + desc.num_tiles as u64); 178 | let total_num_of_bytes = 179 | total_num_of_tiles * StoredTilePlacement::encoded_size() as u64; 180 | 181 | self.seek(std::io::SeekFrom::Start( 182 | (total_num_of_bytes as i64 + pos) as u64, 183 | )) 184 | } 185 | } 186 | } 187 | } 188 | 189 | impl<'a, R: Read + Seek> Iterator for PlacedArchiveReader<'a, R> { 190 | type Item = DecodedTilePlacement; 191 | 192 | fn next(&mut self) -> Option { 193 | let tile_placement: StoredTilePlacement = 194 | match bincode::decode_from_std_read(self, BINCODE_CONFIG) { 195 | Ok(tile_placement) => tile_placement, 196 | Err(_) => return None, 197 | }; 198 | 199 | Some(DecodedTilePlacement { 200 | x: tile_placement.x, 201 | y: tile_placement.y, 202 | ms_since_epoch: tile_placement.ms_since_epoch, 203 | color: *self 204 | .meta 205 | .color_id_to_tuple 206 | .get(&tile_placement.color_index) 207 | .unwrap(), 208 | }) 209 | } 210 | } 211 | 212 | #[cfg(test)] 213 | mod tests { 214 | use std::{ 215 | collections::BTreeMap, 216 | io::{Seek, SeekFrom}, 217 | }; 218 | 219 | use chrono::NaiveDateTime; 220 | use rand::Rng; 221 | use tempfile::NamedTempFile; 222 | 223 | use crate::{structures::StoredTilePlacement, PlacedArchiveReader}; 224 | 225 | #[test] 226 | fn read_trait() { 227 | let writeable_file = NamedTempFile::new().unwrap(); 228 | let readable_file = writeable_file.reopen().unwrap(); 229 | let mut archive_writer = crate::PlacedArchiveWriter::new(writeable_file); 230 | 231 | let canvas_size = 512; 232 | let required_num_of_tile_updates = (canvas_size as u32) * (canvas_size as u32); 233 | 234 | let mut color_id_to_tuple = BTreeMap::new(); 235 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 236 | color_id_to_tuple.insert(1, [255, 0, 0, 255]); 237 | color_id_to_tuple.insert(2, [0, 255, 0, 255]); 238 | color_id_to_tuple.insert(3, [0, 0, 255, 255]); 239 | color_id_to_tuple.insert(4, [255, 255, 0, 255]); 240 | color_id_to_tuple.insert(5, [255, 0, 255, 255]); 241 | color_id_to_tuple.insert(6, [0, 255, 255, 255]); 242 | color_id_to_tuple.insert(7, [255, 255, 255, 255]); 243 | 244 | let mut generator = rand::thread_rng(); 245 | let mut expected_tiles: Vec = Vec::new(); 246 | 247 | for i in 0..required_num_of_tile_updates { 248 | let tile = StoredTilePlacement { 249 | x: generator.gen_range(0..canvas_size), 250 | y: generator.gen_range(0..canvas_size), 251 | color_index: generator.gen_range(0..color_id_to_tuple.len() as u8), 252 | ms_since_epoch: i, 253 | }; 254 | 255 | archive_writer.add_tile( 256 | tile.x, 257 | tile.y, 258 | *color_id_to_tuple.get(&tile.color_index).unwrap(), 259 | NaiveDateTime::from_timestamp_millis(tile.ms_since_epoch as i64).unwrap(), 260 | ); 261 | expected_tiles.push(tile); 262 | } 263 | 264 | archive_writer.finalize(false); 265 | 266 | let reader = PlacedArchiveReader::new(readable_file).unwrap(); 267 | let read_tiles = reader.collect::>(); 268 | for (i, expected_tile) in expected_tiles.into_iter().enumerate() { 269 | let read_tile = &read_tiles[i]; 270 | 271 | assert_eq!(read_tile.x, expected_tile.x); 272 | assert_eq!(read_tile.y, expected_tile.y); 273 | assert_eq!( 274 | read_tile.color, 275 | *color_id_to_tuple.get(&expected_tile.color_index).unwrap() 276 | ); 277 | assert_eq!(read_tile.ms_since_epoch, expected_tile.ms_since_epoch); 278 | } 279 | } 280 | 281 | #[test] 282 | fn seek_trait() { 283 | let writeable_file = NamedTempFile::new().unwrap(); 284 | let readable_file = writeable_file.reopen().unwrap(); 285 | let mut archive_writer = crate::PlacedArchiveWriter::new(writeable_file); 286 | 287 | let canvas_size = 512; 288 | 289 | let mut color_id_to_tuple = BTreeMap::new(); 290 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 291 | color_id_to_tuple.insert(1, [255, 0, 0, 255]); 292 | color_id_to_tuple.insert(2, [0, 255, 0, 255]); 293 | color_id_to_tuple.insert(3, [0, 0, 255, 255]); 294 | color_id_to_tuple.insert(4, [255, 255, 0, 255]); 295 | color_id_to_tuple.insert(5, [255, 0, 255, 255]); 296 | color_id_to_tuple.insert(6, [0, 255, 255, 255]); 297 | color_id_to_tuple.insert(7, [255, 255, 255, 255]); 298 | 299 | let mut generator = rand::thread_rng(); 300 | let mut expected_tiles: Vec = Vec::new(); 301 | 302 | for i in 0..100 { 303 | let tile = StoredTilePlacement { 304 | x: generator.gen_range(0..canvas_size), 305 | y: generator.gen_range(0..canvas_size), 306 | color_index: generator.gen_range(0..color_id_to_tuple.len() as u8), 307 | ms_since_epoch: i, 308 | }; 309 | 310 | archive_writer.add_tile( 311 | tile.x, 312 | tile.y, 313 | *color_id_to_tuple.get(&tile.color_index).unwrap(), 314 | NaiveDateTime::from_timestamp_millis(tile.ms_since_epoch as i64).unwrap(), 315 | ); 316 | expected_tiles.push(tile); 317 | } 318 | 319 | archive_writer.finalize(false); 320 | 321 | let mut reader = PlacedArchiveReader::new(readable_file).unwrap(); 322 | 323 | reader.seek(SeekFrom::Start(0)).unwrap(); 324 | let tile = reader.next().unwrap(); 325 | assert_eq!(tile.ms_since_epoch, 0); 326 | 327 | reader 328 | .seek(SeekFrom::Start(StoredTilePlacement::encoded_size() as u64)) 329 | .unwrap(); 330 | let tile = reader.next().unwrap(); 331 | // Offset is now 2 tiles 332 | assert_eq!(tile.ms_since_epoch, 1); 333 | 334 | let current_pos = reader.seek(SeekFrom::Current(0)).unwrap(); 335 | assert_eq!(current_pos, StoredTilePlacement::encoded_size() as u64 * 2); 336 | 337 | reader 338 | .seek(SeekFrom::Current(StoredTilePlacement::encoded_size() as i64)) 339 | .unwrap(); 340 | let tile = reader.next().unwrap(); 341 | // Offset is now 4 tiles 342 | assert_eq!(tile.ms_since_epoch, 3); 343 | 344 | reader 345 | .seek(SeekFrom::Current( 346 | StoredTilePlacement::encoded_size() as i64 * -2, 347 | )) 348 | .unwrap(); 349 | let tile = reader.next().unwrap(); 350 | // Offset is now 3 tiles 351 | assert_eq!(tile.ms_since_epoch, 2); 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /archive/src/archive_writer.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::BTreeMap, 3 | io::{Read, Seek, SeekFrom, Write}, 4 | }; 5 | 6 | // todo: try different compression (frame grid, encode each second in an array, even if nothing changed) 7 | use chrono::NaiveDateTime; 8 | use image::RgbImage; 9 | use mla::{config::ArchiveWriterConfig, ArchiveWriter}; 10 | use tempfile::tempfile; 11 | 12 | use crate::{ 13 | constants::BINCODE_CONFIG, 14 | structures::{CanvasSizeChange, ChunkDescription, Meta, StoredTilePlacement}, 15 | }; 16 | 17 | // todo: make parameter 18 | const NUM_CHUNKS: u32 = 64; 19 | 20 | #[derive(Debug, PartialEq, Eq)] 21 | struct IntermediateTilePlacement { 22 | pub x: u16, 23 | pub y: u16, 24 | pub placed_at: NaiveDateTime, 25 | pub color_index: u8, 26 | } 27 | 28 | impl PartialOrd for IntermediateTilePlacement { 29 | fn partial_cmp(&self, other: &Self) -> Option { 30 | Some(self.placed_at.cmp(&other.placed_at)) 31 | } 32 | } 33 | 34 | impl Ord for IntermediateTilePlacement { 35 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 36 | self.placed_at.cmp(&other.placed_at) 37 | } 38 | } 39 | 40 | pub struct PlacedArchiveWriter<'a, W: Write> { 41 | mla: ArchiveWriter<'a, W>, 42 | color_tuple_to_id: BTreeMap<[u8; 4], u8>, 43 | tile_placements: Vec, 44 | } 45 | 46 | impl<'a, W: Write> PlacedArchiveWriter<'a, W> { 47 | pub fn new(dest: W) -> Self { 48 | let mut config = ArchiveWriterConfig::new(); 49 | config.disable_layer(mla::Layers::ENCRYPT); 50 | // todo: enable compression? 51 | let mla = ArchiveWriter::from_config(dest, config).unwrap(); 52 | 53 | PlacedArchiveWriter { 54 | mla, 55 | color_tuple_to_id: BTreeMap::new(), 56 | tile_placements: Vec::new(), 57 | } 58 | } 59 | 60 | pub fn add_tile(&mut self, x: u16, y: u16, color: [u8; 4], placed_at: NaiveDateTime) { 61 | let color_map_len = self.color_tuple_to_id.len() as u8; 62 | let color_index = self 63 | .color_tuple_to_id 64 | .entry(color) 65 | .or_insert_with(|| color_map_len); 66 | 67 | self.tile_placements.push(IntermediateTilePlacement { 68 | x, 69 | y, 70 | placed_at, 71 | color_index: *color_index, 72 | }); 73 | } 74 | 75 | pub fn finalize(&mut self, should_generate_snapshots: bool) { 76 | self.tile_placements.sort(); 77 | 78 | let first_tile_placed_at = self.tile_placements.first().unwrap().placed_at; 79 | let num_of_tiles_in_chunk = self.tile_placements.len() as u32 / NUM_CHUNKS; 80 | 81 | let mut chunk_descs: Vec = Vec::new(); 82 | 83 | for (i, tiles) in self 84 | .tile_placements 85 | .chunks(num_of_tiles_in_chunk as usize) 86 | .enumerate() 87 | { 88 | let mut tile_buf = Vec::new(); 89 | 90 | for tile in tiles { 91 | bincode::encode_into_std_write( 92 | StoredTilePlacement { 93 | x: tile.x, 94 | y: tile.y, 95 | ms_since_epoch: tile 96 | .placed_at 97 | .signed_duration_since(first_tile_placed_at) 98 | .num_milliseconds() as u32, 99 | color_index: tile.color_index, 100 | }, 101 | &mut tile_buf, 102 | BINCODE_CONFIG, 103 | ) 104 | .unwrap(); 105 | } 106 | 107 | self.mla 108 | .add_file( 109 | format!("tiles/{}", i).as_str(), 110 | tile_buf.len() as u64, 111 | tile_buf.as_slice(), 112 | ) 113 | .unwrap(); 114 | 115 | chunk_descs.push(ChunkDescription { 116 | id: i as u32, 117 | up_to_ms_since_epoch: tiles 118 | .last() 119 | .unwrap() 120 | .placed_at 121 | .signed_duration_since(first_tile_placed_at) 122 | .num_milliseconds() as u32, 123 | num_tiles: tiles.len() as u32, 124 | }); 125 | } 126 | 127 | // todo 128 | let canvas_size_changes = vec![CanvasSizeChange { 129 | ms_since_epoch: 0, 130 | width: 2000, 131 | height: 2000, 132 | }]; 133 | 134 | let meta = Meta { 135 | canvas_size_changes, 136 | chunk_descs, 137 | last_tile_placed_at_ms_since_epoch: self 138 | .tile_placements 139 | .last() 140 | .unwrap() 141 | .placed_at 142 | .signed_duration_since(first_tile_placed_at) 143 | .num_milliseconds() as u32, 144 | total_tile_placements: self.tile_placements.len() as u64, 145 | color_id_to_tuple: BTreeMap::from_iter( 146 | self.color_tuple_to_id.iter().map(|(k, v)| (*v, *k)), 147 | ), 148 | }; 149 | 150 | let mut meta_buf = Vec::new(); 151 | bincode::encode_into_std_write(meta.clone(), &mut meta_buf, BINCODE_CONFIG).unwrap(); 152 | self.mla 153 | .add_file("meta", meta_buf.len() as u64, meta_buf.as_slice()) 154 | .unwrap(); 155 | 156 | // Generate snapshots 157 | if should_generate_snapshots { 158 | let largest_canvas_size = meta.get_largest_canvas_size().unwrap(); 159 | let mut canvas = RgbImage::new( 160 | largest_canvas_size.width as u32, 161 | largest_canvas_size.height as u32, 162 | ); 163 | canvas.fill(0xff); 164 | 165 | let mut num_of_processed_tiles = 0; 166 | for chunk in meta.chunk_descs { 167 | for tile in self.tile_placements[num_of_processed_tiles..] 168 | .iter() 169 | .take(chunk.num_tiles as usize) 170 | { 171 | canvas.put_pixel( 172 | tile.x as u32, 173 | tile.y as u32, 174 | image::Rgb( 175 | meta.color_id_to_tuple[&tile.color_index][0..3] 176 | .try_into() 177 | .unwrap(), 178 | ), 179 | ); 180 | } 181 | 182 | num_of_processed_tiles += chunk.num_tiles as usize; 183 | 184 | let mut temp_snapshot = tempfile().unwrap(); 185 | let mut buf = Vec::new(); 186 | // needs a seekable writer 187 | canvas 188 | .write_to(&mut temp_snapshot, image::ImageOutputFormat::Png) 189 | .unwrap(); 190 | temp_snapshot.seek(SeekFrom::Start(0)).unwrap(); 191 | temp_snapshot.read_to_end(&mut buf).unwrap(); 192 | 193 | self.mla 194 | .add_file( 195 | format!("snapshots/{}", chunk.id).as_str(), 196 | buf.len() as u64, 197 | buf.as_slice(), 198 | ) 199 | .unwrap(); 200 | } 201 | } 202 | 203 | self.mla.finalize().unwrap(); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /archive/src/constants.rs: -------------------------------------------------------------------------------- 1 | use bincode::config::{Configuration, Fixint, LittleEndian, NoLimit, WriteFixedArrayLength}; 2 | 3 | // Use legacy encoding for fixed-width integers (field size needs to be constant so we can seek) 4 | pub const BINCODE_CONFIG: Configuration = 5 | bincode::config::legacy(); 6 | -------------------------------------------------------------------------------- /archive/src/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum PlacedArchiveError { 3 | MLAReadError(mla::errors::Error), 4 | MissingMetaFile, 5 | CouldNotDecodeMetaFile, 6 | } 7 | 8 | #[derive(Debug)] 9 | pub enum NextTileChunkError { 10 | OutOfChunks, 11 | MissingChunkFile, 12 | CouldNotFetchChunkFile(mla::errors::Error), 13 | } 14 | -------------------------------------------------------------------------------- /archive/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod archive_reader; 2 | mod archive_writer; 3 | mod constants; 4 | mod errors; 5 | pub mod structures; 6 | 7 | pub use crate::archive_reader::PlacedArchiveReader; 8 | pub use crate::archive_writer::PlacedArchiveWriter; 9 | -------------------------------------------------------------------------------- /archive/src/structures.rs: -------------------------------------------------------------------------------- 1 | use bincode::{Decode, Encode}; 2 | use std::{collections::BTreeMap, io::Write}; 3 | 4 | use crate::constants::BINCODE_CONFIG; 5 | 6 | #[derive(Encode, Decode, PartialEq, Eq, Debug)] 7 | #[repr(C)] 8 | pub struct StoredTilePlacement { 9 | pub x: u16, 10 | pub y: u16, 11 | pub color_index: u8, 12 | pub ms_since_epoch: u32, 13 | } 14 | 15 | impl StoredTilePlacement { 16 | pub fn encoded_size() -> usize { 17 | let mut buf = Vec::new(); 18 | bincode::encode_into_std_write( 19 | StoredTilePlacement { 20 | x: 0, 21 | y: 0, 22 | ms_since_epoch: 0, 23 | color_index: 0, 24 | }, 25 | &mut buf, 26 | BINCODE_CONFIG, 27 | ) 28 | .unwrap(); 29 | 30 | buf.len() 31 | } 32 | 33 | pub fn write_into(&self, w: &mut impl Write) { 34 | bincode::encode_into_std_write(self, w, BINCODE_CONFIG).unwrap(); 35 | } 36 | } 37 | 38 | #[derive(PartialEq, Eq, Debug)] 39 | pub struct DecodedTilePlacement { 40 | pub x: u16, 41 | pub y: u16, 42 | pub ms_since_epoch: u32, 43 | /// rgba 44 | pub color: [u8; 4], 45 | } 46 | 47 | #[derive(Encode, Decode, PartialEq, Eq, Debug, Clone)] 48 | pub struct CanvasSizeChange { 49 | pub width: u16, 50 | pub height: u16, 51 | pub ms_since_epoch: u32, 52 | } 53 | 54 | #[derive(Encode, Decode, PartialEq, Eq, Debug, Clone)] 55 | pub struct ChunkDescription { 56 | pub id: u32, 57 | pub up_to_ms_since_epoch: u32, 58 | pub num_tiles: u32, 59 | } 60 | 61 | #[derive(Encode, Decode, PartialEq, Eq, Debug, Clone)] 62 | pub struct Meta { 63 | pub canvas_size_changes: Vec, 64 | pub total_tile_placements: u64, 65 | pub last_tile_placed_at_ms_since_epoch: u32, 66 | /// rgba 67 | pub color_id_to_tuple: BTreeMap, 68 | pub chunk_descs: Vec, 69 | } 70 | 71 | impl Meta { 72 | pub fn get_largest_canvas_size(&self) -> Option { 73 | Some( 74 | self.canvas_size_changes 75 | .iter() 76 | .max_by_key(|x| (x.width as u32) * (x.height as u32))? 77 | .clone(), 78 | ) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | archive = { path = "../archive" } 8 | player = { path = "../player" } 9 | clap = { version = "4.0.29", features = ["derive"] } 10 | byte-unit = "4.0.18" 11 | chrono = "0.4.23" 12 | csv = "1.1.6" 13 | colors-transform = "0.2.11" 14 | image = "0.24.5" 15 | proc-macro2 = { version = "=1.0.60" } 16 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use archive::{PlacedArchiveReader, PlacedArchiveWriter}; 2 | use chrono::NaiveDateTime; 3 | use clap::{Parser, Subcommand}; 4 | use colors_transform::Color; 5 | use std::fs::File; 6 | 7 | // todo: use https://github.com/emersonford/tracing-indicatif for automatic progress bars? 8 | 9 | #[derive(Parser, Debug)] 10 | #[command(author, version, about, long_about = None)] 11 | #[command(propagate_version = true)] 12 | struct Cli { 13 | #[command(subcommand)] 14 | command: Commands, 15 | } 16 | 17 | #[derive(Debug, Subcommand)] 18 | enum Commands { 19 | /// Repack data from a CSV into an archive containing color and tile data 20 | Pack { in_file: String, out_file: String }, 21 | /// Render history to an image 22 | Render { 23 | archive_path: String, 24 | out_file: String, 25 | #[clap(short, long, default_value = "0")] 26 | /// if 0, render all history 27 | up_to_seconds: u32, 28 | }, 29 | Play { 30 | archive_path: String, 31 | #[clap(short, long, default_value = "1")] 32 | timescale_factor: f32, 33 | }, 34 | } 35 | 36 | fn main() { 37 | let cli = Cli::parse(); 38 | 39 | match cli.command { 40 | Commands::Pack { in_file, out_file } => { 41 | let file = File::open(in_file).expect("Could not open file"); 42 | let mut reader = csv::Reader::from_reader(file); 43 | 44 | let out_file = File::create(out_file).expect("Could not create file"); 45 | let mut archive_writer = PlacedArchiveWriter::new(out_file); 46 | 47 | for result in reader.records() { 48 | let record = result.expect("Could not read record"); 49 | 50 | let placed_at = NaiveDateTime::parse_from_str( 51 | record.get(0).unwrap(), 52 | "%Y-%m-%d %H:%M:%S%.3f UTC", 53 | ) 54 | .expect("Could not parse timestamp"); 55 | 56 | let color_str = record.get(2).unwrap().to_string(); 57 | let parsed_color = colors_transform::Rgb::from_hex_str(&color_str).unwrap(); 58 | 59 | let clean_coords = record.get(3).unwrap().replace('"', ""); 60 | // todo: handle moderator edits 61 | if clean_coords.matches(',').count() != 1 { 62 | println!("Invalid coordinates: {}", clean_coords); 63 | continue; 64 | } 65 | let mut coords = clean_coords.split(','); 66 | let x_str = coords.next().unwrap(); 67 | let y_str = coords.next().unwrap(); 68 | let x = x_str.parse::().expect("Could not parse x coordinate"); 69 | let y = y_str.parse::().expect("Could not parse y coordinate"); 70 | 71 | archive_writer.add_tile( 72 | x, 73 | y, 74 | [ 75 | parsed_color.get_red() as u8, 76 | parsed_color.get_green() as u8, 77 | parsed_color.get_blue() as u8, 78 | 0xff, 79 | ], 80 | placed_at, 81 | ); 82 | } 83 | 84 | archive_writer.finalize(true); 85 | } 86 | Commands::Render { 87 | archive_path, 88 | out_file, 89 | up_to_seconds, 90 | } => { 91 | let file = File::open(archive_path).expect("Could not open file"); 92 | let reader = PlacedArchiveReader::new(file).expect("Could not read archive"); 93 | let canvas_size = reader.meta.get_largest_canvas_size().unwrap(); 94 | 95 | let mut canvas = 96 | image::RgbaImage::new(canvas_size.width as u32, canvas_size.height as u32); 97 | 98 | canvas.fill(0xff); 99 | 100 | for tile in reader { 101 | if tile.ms_since_epoch > up_to_seconds * 1000 && up_to_seconds != 0 { 102 | break; 103 | } 104 | 105 | canvas.put_pixel(tile.x as u32, tile.y as u32, image::Rgba(tile.color)); 106 | } 107 | 108 | canvas.save(out_file).expect("Could not save image"); 109 | } 110 | Commands::Play { 111 | archive_path, 112 | timescale_factor, 113 | } => { 114 | player::play(archive_path, timescale_factor); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /player/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "player" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | archive = { path = "../archive" } 10 | colors-transform = "0.2.11" 11 | game-loop = { version = "0.10.0", features = ["winit"] } 12 | wgpu = "0.15.0" 13 | winit = "0.27" 14 | winit_input_helper = "0.13" 15 | pollster = "0.2" 16 | ultraviolet = "0.9.0" 17 | bytemuck = "1.12.3" 18 | num = "0.4.0" 19 | futures-intrusive = "0.5.0" 20 | 21 | [dev-dependencies] 22 | rand = "0.8.5" 23 | image = "0.24.5" 24 | log = "0.4.17" 25 | env_logger = "0.10.0" 26 | -------------------------------------------------------------------------------- /player/shaders/scale.wgsl: -------------------------------------------------------------------------------- 1 | // Vertex shader bindings 2 | 3 | struct VertexOutput { 4 | @location(0) tex_coord: vec2, 5 | @builtin(position) position: vec4, 6 | } 7 | 8 | struct Locals { 9 | transform: mat4x4, 10 | } 11 | @group(0) @binding(2) var r_locals: Locals; 12 | 13 | @vertex 14 | fn vs_main( 15 | @location(0) position: vec2, 16 | ) -> VertexOutput { 17 | var out: VertexOutput; 18 | out.tex_coord = vec2(position.x, 1.0 - position.y); 19 | out.position = r_locals.transform * vec4(position, 0.0, 1.0); 20 | return out; 21 | } 22 | 23 | // Fragment shader bindings 24 | 25 | @group(0) @binding(0) var r_tex_color: texture_2d; 26 | @group(0) @binding(1) var r_tex_sampler: sampler; 27 | 28 | @fragment 29 | fn fs_main(@location(0) tex_coord: vec2) -> @location(0) vec4 { 30 | return textureSample(r_tex_color, r_tex_sampler, tex_coord); 31 | } 32 | -------------------------------------------------------------------------------- /player/shaders/texture_update_by_coords.compute.wgsl: -------------------------------------------------------------------------------- 1 | const SIZE_OF_COORDINATE_UPDATE_BYTES = 9u; 2 | 3 | struct FourTileUpdate { 4 | data: array 5 | }; 6 | 7 | struct DecodedTileUpdate { 8 | x: u32, 9 | y: u32, 10 | color_index: u32, 11 | ms_since_epoch: u32, 12 | }; 13 | 14 | // todo: use https://docs.rs/crevice/latest/crevice/ for more ergonomic struct? 15 | struct Locals { 16 | color_map: array, 256>, 17 | width: u32, 18 | height: u32, 19 | }; 20 | 21 | struct BoundsInChunk { 22 | requested_up_to_ms_since_epoch: u32, 23 | max_ms_since_epoch_seen: atomic, 24 | max_ms_since_epoch_used: atomic, 25 | max_index_in_chunk_used: atomic, 26 | } 27 | 28 | @group(0) @binding(0) var tile_updates : array; 29 | @group(0) @binding(1) var r_locals : Locals; 30 | @group(0) @binding(2) var last_index_for_tile : array>; 31 | @group(0) @binding(3) var bounds : BoundsInChunk; 32 | @group(0) @binding(4) var texture_out : texture_storage_2d; 33 | 34 | fn readU8(i: u32, current_offset: u32) -> u32 { 35 | var ipos : u32 = current_offset / 4u; 36 | var val_u32 : u32 = tile_updates[i].data[ipos]; 37 | var shift : u32 = 8u * (current_offset % 4u); 38 | var val_u8 : u32 = (val_u32 >> shift) & 0xFFu; 39 | 40 | return val_u8; 41 | } 42 | 43 | fn readU16(i: u32, current_offset: u32) -> u32 { 44 | var first = readU8(i, current_offset); 45 | var second = readU8(i, current_offset + 1u); 46 | var value = first | (second << 8u); 47 | 48 | return value; 49 | } 50 | 51 | fn readU32(i: u32, current_offset: u32) -> u32 { 52 | var first = readU16(i, current_offset); 53 | var second = readU16(i, current_offset + 2u); 54 | var value = first | (second << 16u); 55 | 56 | return value; 57 | } 58 | 59 | fn readTile(four_tile_offset: u32, offset_in_four_tiles: u32) -> DecodedTileUpdate { 60 | var current_offset = offset_in_four_tiles * SIZE_OF_COORDINATE_UPDATE_BYTES; 61 | 62 | var tile: DecodedTileUpdate; 63 | 64 | tile.x = readU16(four_tile_offset, current_offset); 65 | current_offset += 2u; 66 | tile.y = readU16(four_tile_offset, current_offset); 67 | current_offset += 2u; 68 | tile.color_index = readU8(four_tile_offset, current_offset); 69 | current_offset += 1u; 70 | tile.ms_since_epoch = readU32(four_tile_offset, current_offset); 71 | 72 | return tile; 73 | } 74 | 75 | fn getTileIndex(tile: DecodedTileUpdate) -> u32 { 76 | return tile.x + (tile.y * r_locals.height); 77 | } 78 | 79 | fn getDataIndexForInvocation(id: vec3) -> u32 { 80 | return (id.x * 4u) + id.y; 81 | } 82 | 83 | @compute 84 | @workgroup_size(1) 85 | fn calculate_final_tiles(@builtin(global_invocation_id) id: vec3) { 86 | if (getDataIndexForInvocation(id) >= arrayLength(&tile_updates)) { 87 | return; 88 | } 89 | 90 | let tile = readTile(id.x, id.y); 91 | 92 | if (tile.color_index == 255u) { 93 | // This update is just padding 94 | return; 95 | } 96 | 97 | atomicMax(&bounds.max_ms_since_epoch_seen, tile.ms_since_epoch); 98 | 99 | if (tile.ms_since_epoch > bounds.requested_up_to_ms_since_epoch) { 100 | return; 101 | } 102 | 103 | atomicMax(&bounds.max_ms_since_epoch_used, tile.ms_since_epoch); 104 | atomicMax(&bounds.max_index_in_chunk_used, getDataIndexForInvocation(id)); 105 | atomicMax(&last_index_for_tile[getTileIndex(tile)], getDataIndexForInvocation(id)); 106 | } 107 | 108 | @compute 109 | @workgroup_size(1) 110 | fn update_texture(@builtin(global_invocation_id) id: vec3) { 111 | if (getDataIndexForInvocation(id) >= arrayLength(&tile_updates)) { 112 | return; 113 | } 114 | 115 | let tile = readTile(id.x, id.y); 116 | 117 | if (tile.color_index == 255u) { 118 | // This update is just padding 119 | return; 120 | } 121 | 122 | if (tile.ms_since_epoch > bounds.requested_up_to_ms_since_epoch) { 123 | return; 124 | } 125 | 126 | let max_data_index_for_tile = last_index_for_tile[getTileIndex(tile)]; 127 | 128 | if (getDataIndexForInvocation(id) != max_data_index_for_tile) { 129 | return; 130 | } 131 | 132 | let color = r_locals.color_map[tile.color_index]; 133 | 134 | textureStore( 135 | texture_out, 136 | vec2(i32(tile.x), i32(tile.y)), 137 | vec4(f32(color.x) / 255.0, f32(color.y) / 255.0, f32(color.z) / 255.0, f32(color.w) / 255.0) 138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /player/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{BufReader, Cursor, Read, Seek}, 4 | time::Duration, 5 | }; 6 | 7 | use archive::{structures::StoredTilePlacement, PlacedArchiveReader}; 8 | use game_loop::{game_loop, Time, TimeTrait}; 9 | use winit::{ 10 | dpi::{LogicalSize, PhysicalSize}, 11 | event::{Event, WindowEvent}, 12 | event_loop::EventLoop, 13 | window::WindowBuilder, 14 | }; 15 | use winit_input_helper::WinitInputHelper; 16 | 17 | mod pixel_art_display_state; 18 | mod renderers; 19 | mod texture_update_by_coords; 20 | mod transform_generator; 21 | 22 | struct Player { 23 | rendered_up_to: Duration, 24 | render_state: pixel_art_display_state::PixelArtDisplayState, 25 | transform_generator: transform_generator::TransformGenerator, 26 | timescale_factor: f32, 27 | } 28 | 29 | impl Player { 30 | pub fn new( 31 | render_state: pixel_art_display_state::PixelArtDisplayState, 32 | timescale_factor: f32, 33 | window_size: PhysicalSize, 34 | ) -> Self { 35 | let texture_size = render_state.texture_size.clone(); 36 | Self { 37 | rendered_up_to: Duration::ZERO, 38 | render_state, 39 | timescale_factor, 40 | transform_generator: transform_generator::TransformGenerator::new( 41 | window_size.width, 42 | window_size.height, 43 | texture_size, 44 | ), 45 | } 46 | } 47 | 48 | pub fn update(&mut self, dt: Duration) { 49 | self.rendered_up_to += dt * self.timescale_factor as u32; 50 | 51 | self.render_state 52 | .update(self.rendered_up_to.as_millis() as u32); 53 | } 54 | 55 | pub fn draw(&mut self) { 56 | self.transform_generator.update(); 57 | self.render_state 58 | .render(self.transform_generator.get_transform_matrix()); 59 | } 60 | 61 | pub fn handle_input(&mut self, input: &WinitInputHelper) { 62 | let scrolled = input.scroll_diff(); 63 | 64 | if scrolled != 0.0 { 65 | self.transform_generator 66 | .apply_scale_diff(scrolled, input.mouse()); 67 | } 68 | 69 | if input.mouse_pressed(0) { 70 | self.transform_generator.on_pan_start(); 71 | } 72 | 73 | if input.mouse_released(0) { 74 | self.transform_generator.on_pan_end(); 75 | } 76 | 77 | if input.mouse_held(0) { 78 | let (x, y) = input.mouse_diff(); 79 | self.transform_generator.apply_translate_diff(x, -y) 80 | } 81 | } 82 | 83 | pub fn resize(&mut self, size: PhysicalSize) { 84 | self.render_state.on_window_resize(size.width, size.height); 85 | self.transform_generator 86 | .on_window_resize(size.width, size.height) 87 | } 88 | 89 | pub fn on_scale_factor_changed(&mut self, scale_factor: f64) { 90 | self.transform_generator 91 | .set_window_scale_factor(scale_factor as f32) 92 | } 93 | } 94 | 95 | // todo: add option to unlock fps? 96 | pub const FPS: usize = 60; 97 | pub const TIME_STEP: Duration = Duration::from_nanos(1_000_000_000 / FPS as u64); 98 | 99 | // todo: make dynamic 100 | const WIDTH: u32 = 2000; 101 | const HEIGHT: u32 = 2000; 102 | 103 | pub fn play(archive_path: String, timescale_factor: f32) -> i32 { 104 | let event_loop = EventLoop::new(); 105 | let mut input = WinitInputHelper::new(); 106 | 107 | let window = { 108 | // todo: why is / 2 needed 109 | let size = LogicalSize::new(WIDTH as f64 / 2.0, HEIGHT as f64 / 2.0); 110 | WindowBuilder::new() 111 | .with_title("Placed") 112 | .with_min_inner_size(size) 113 | .build(&event_loop) 114 | .unwrap() 115 | }; 116 | 117 | let file = File::open(archive_path).expect("Failed to open archive"); 118 | let reader = PlacedArchiveReader::new(file).expect("Failed to create reader"); 119 | 120 | let mut state = 121 | pixel_art_display_state::PixelArtDisplayState::new(&window, reader.meta.clone(), reader); 122 | state.clear(wgpu::Color::WHITE); 123 | let p = Player::new(state, timescale_factor, window.inner_size()); 124 | 125 | game_loop( 126 | event_loop, 127 | window, 128 | p, 129 | FPS as u32, 130 | 2.0, 131 | move |g| { 132 | g.game.update(TIME_STEP); 133 | }, 134 | move |g| { 135 | g.game.draw(); 136 | 137 | let dt = TIME_STEP.as_secs_f64() - Time::now().sub(&g.current_instant()); 138 | if dt > 0.0 { 139 | std::thread::sleep(Duration::from_secs_f64(dt)); 140 | } 141 | }, 142 | move |g, event| { 143 | if input.update(event) { 144 | g.game.handle_input(&input); 145 | } 146 | 147 | match event { 148 | Event::WindowEvent { event, .. } => match event { 149 | WindowEvent::Resized(physical_size) => { 150 | g.game.resize(*physical_size); 151 | } 152 | WindowEvent::ScaleFactorChanged { 153 | new_inner_size, 154 | scale_factor, 155 | .. 156 | } => { 157 | g.game.on_scale_factor_changed(*scale_factor); 158 | g.game.resize(**new_inner_size); 159 | } 160 | WindowEvent::CloseRequested => { 161 | std::process::exit(0); 162 | } 163 | _ => (), 164 | }, 165 | _ => {} 166 | }; 167 | }, 168 | ); 169 | } 170 | -------------------------------------------------------------------------------- /player/src/pixel_art_display_state.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Seek}, 3 | time::Duration, 4 | }; 5 | 6 | use crate::{ 7 | renderers::ScalingRenderer, 8 | texture_update_by_coords::{PartialUpdateResult, TextureUpdateByCoords}, 9 | }; 10 | use archive::structures::Meta; 11 | use ultraviolet::Mat4; 12 | use wgpu::{Adapter, Device, Instance, Queue, Surface}; 13 | use winit::window::Window; 14 | 15 | pub struct PixelArtDisplayState { 16 | surface: Surface, 17 | adapter: Adapter, 18 | device: Device, 19 | queue: Queue, 20 | 21 | /// A default renderer to scale the input texture to the screen size (stolen from the pixels crate) 22 | scaling_renderer: ScalingRenderer, 23 | compute_renderer: TextureUpdateByCoords, 24 | last_up_to_ms: u32, 25 | up_to_ms: u32, 26 | 27 | pub texture_size: wgpu::Extent3d, 28 | } 29 | 30 | impl PixelArtDisplayState { 31 | pub fn new(window: &Window, meta: Meta, reader: R) -> Self { 32 | pollster::block_on(Self::async_new(window, meta, reader)) 33 | } 34 | 35 | async fn async_new(window: &Window, meta: Meta, reader: R) -> Self { 36 | let instance = Instance::new(wgpu::InstanceDescriptor::default()); 37 | 38 | let surface = unsafe { instance.create_surface(&window).unwrap() }; 39 | let adapter = instance 40 | .request_adapter(&wgpu::RequestAdapterOptions { 41 | power_preference: wgpu::PowerPreference::LowPower, 42 | compatible_surface: Some(&surface), 43 | force_fallback_adapter: false, 44 | }) 45 | .await 46 | .unwrap(); 47 | 48 | let (device, queue) = adapter 49 | .request_device( 50 | &wgpu::DeviceDescriptor { 51 | features: wgpu::Features::empty(), 52 | limits: wgpu::Limits::default(), 53 | label: None, 54 | }, 55 | None, 56 | ) 57 | .await 58 | .unwrap(); 59 | 60 | let config = wgpu::SurfaceConfiguration { 61 | usage: wgpu::TextureUsages::RENDER_ATTACHMENT, 62 | view_formats: [surface.get_capabilities(&adapter).formats[0]].to_vec(), 63 | format: surface.get_capabilities(&adapter).formats[0], 64 | width: window.inner_size().width, 65 | height: window.inner_size().height, 66 | present_mode: wgpu::PresentMode::Fifo, 67 | alpha_mode: wgpu::CompositeAlphaMode::Auto, 68 | }; 69 | surface.configure(&device, &config); 70 | 71 | let canvas_size = meta 72 | .get_largest_canvas_size() 73 | .expect("No canvas size found in meta"); 74 | 75 | let texture_extent = wgpu::Extent3d { 76 | width: canvas_size.width.into(), 77 | height: canvas_size.height.into(), 78 | depth_or_array_layers: 1, 79 | }; 80 | 81 | let surface_texture_format = *surface 82 | .get_capabilities(&adapter) 83 | .formats 84 | .first() 85 | .unwrap_or(&wgpu::TextureFormat::Bgra8UnormSrgb); 86 | 87 | let compute_renderer = TextureUpdateByCoords::new(&device, meta, reader, None); 88 | 89 | let scaling_renderer = ScalingRenderer::new( 90 | &device, 91 | &compute_renderer.texture_view, 92 | surface_texture_format, 93 | wgpu::Color::BLACK, 94 | wgpu::BlendState::REPLACE, 95 | ); 96 | 97 | Self { 98 | surface, 99 | adapter, 100 | device, 101 | queue, 102 | scaling_renderer, 103 | compute_renderer, 104 | last_up_to_ms: 0, 105 | up_to_ms: 0, 106 | texture_size: texture_extent, 107 | } 108 | } 109 | 110 | pub fn update(&mut self, up_to_ms: u32) { 111 | self.last_up_to_ms = self.up_to_ms; 112 | self.up_to_ms = up_to_ms; 113 | 114 | let diff = Duration::from_millis((self.up_to_ms - self.last_up_to_ms).into()); 115 | 116 | match self 117 | .compute_renderer 118 | .update(&self.device, &self.queue, self.up_to_ms, diff) 119 | { 120 | PartialUpdateResult::ReachedEndOfInput => { 121 | // temp 122 | panic!("Reached end of input"); 123 | } 124 | PartialUpdateResult::UpdatedUpToMs { 125 | max_ms_since_epoch_used, 126 | did_update_up_to_requested_ms, 127 | } => {} 128 | } 129 | } 130 | 131 | pub fn render(&mut self, transform: Mat4) { 132 | let frame = self.surface.get_current_texture().unwrap(); 133 | 134 | let view = frame 135 | .texture 136 | .create_view(&wgpu::TextureViewDescriptor::default()); 137 | 138 | let mut encoder = self 139 | .device 140 | .create_command_encoder(&wgpu::CommandEncoderDescriptor { 141 | label: Some("render_encoder"), 142 | }); 143 | 144 | self.scaling_renderer 145 | .update_transform_matrix(&self.queue, transform); 146 | 147 | self.scaling_renderer.render(&mut encoder, &view); 148 | self.queue.submit(Some(encoder.finish())); 149 | 150 | frame.present(); 151 | } 152 | 153 | pub fn clear(&mut self, color: wgpu::Color) { 154 | let mut encoder = self 155 | .device 156 | .create_command_encoder(&wgpu::CommandEncoderDescriptor { 157 | label: Some("clear_encoder"), 158 | }); 159 | 160 | { 161 | let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 162 | label: Some("Clear render pass"), 163 | color_attachments: &[Some(wgpu::RenderPassColorAttachment { 164 | view: &self.compute_renderer.texture_view, 165 | resolve_target: None, 166 | ops: wgpu::Operations { 167 | load: wgpu::LoadOp::Clear(color), 168 | store: true, 169 | }, 170 | })], 171 | depth_stencil_attachment: None, 172 | }); 173 | } 174 | 175 | self.queue.submit(std::iter::once(encoder.finish())); 176 | } 177 | 178 | pub fn on_window_resize(&mut self, new_width: u32, new_height: u32) { 179 | self.surface.configure( 180 | &self.device, 181 | &wgpu::SurfaceConfiguration { 182 | usage: wgpu::TextureUsages::RENDER_ATTACHMENT, 183 | view_formats: [self.surface.get_capabilities(&self.adapter).formats[0]].to_vec(), 184 | format: self.surface.get_capabilities(&self.adapter).formats[0], 185 | width: new_width, 186 | height: new_height, 187 | present_mode: wgpu::PresentMode::Fifo, 188 | alpha_mode: wgpu::CompositeAlphaMode::Auto, 189 | }, 190 | ); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /player/src/renderers.rs: -------------------------------------------------------------------------------- 1 | use ultraviolet::Mat4; 2 | use wgpu::util::DeviceExt; 3 | 4 | /// A logical texture size for a window surface. 5 | #[derive(Debug)] 6 | pub struct SurfaceSize { 7 | pub width: u32, 8 | pub height: u32, 9 | } 10 | 11 | /// The default renderer that scales your frame to the screen size. 12 | #[derive(Debug)] 13 | pub struct ScalingRenderer { 14 | vertex_buffer: wgpu::Buffer, 15 | uniform_buffer: wgpu::Buffer, 16 | bind_group: wgpu::BindGroup, 17 | render_pipeline: wgpu::RenderPipeline, 18 | pub(crate) clear_color: wgpu::Color, 19 | } 20 | 21 | impl ScalingRenderer { 22 | pub(crate) fn new( 23 | device: &wgpu::Device, 24 | texture_view: &wgpu::TextureView, 25 | render_texture_format: wgpu::TextureFormat, 26 | clear_color: wgpu::Color, 27 | blend_state: wgpu::BlendState, 28 | ) -> Self { 29 | let shader = wgpu::include_wgsl!("../shaders/scale.wgsl"); 30 | let module = device.create_shader_module(shader); 31 | 32 | // Create a texture sampler with nearest neighbor 33 | let sampler = device.create_sampler(&wgpu::SamplerDescriptor { 34 | label: Some("pixels_scaling_renderer_sampler"), 35 | address_mode_u: wgpu::AddressMode::ClampToEdge, 36 | address_mode_v: wgpu::AddressMode::ClampToEdge, 37 | address_mode_w: wgpu::AddressMode::ClampToEdge, 38 | mag_filter: wgpu::FilterMode::Nearest, 39 | min_filter: wgpu::FilterMode::Nearest, 40 | mipmap_filter: wgpu::FilterMode::Nearest, 41 | lod_min_clamp: 0.0, 42 | lod_max_clamp: 1.0, 43 | compare: None, 44 | anisotropy_clamp: None, 45 | border_color: None, 46 | }); 47 | 48 | // Create vertex buffer; array-of-array of position and texture coordinates 49 | let vertex_data: [[f32; 2]; 6] = [ 50 | // Quad 51 | // [-0.5, -0.5], 52 | // [0.5, -0.5], 53 | // [0.5, 0.5], 54 | // [-0.5, -0.5], 55 | // [-0.5, 0.5], 56 | // [0.5, 0.5], 57 | [0.0, 0.0], 58 | [1.0, 0.0], 59 | [1.0, 1.0], 60 | [0.0, 0.0], 61 | [0.0, 1.0], 62 | [1.0, 1.0], 63 | ]; 64 | let vertex_data_slice = bytemuck::cast_slice(&vertex_data); 65 | let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 66 | label: Some("pixels_scaling_renderer_vertex_buffer"), 67 | contents: vertex_data_slice, 68 | usage: wgpu::BufferUsages::VERTEX, 69 | }); 70 | let vertex_buffer_layout = wgpu::VertexBufferLayout { 71 | array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress, 72 | step_mode: wgpu::VertexStepMode::Vertex, 73 | attributes: &[wgpu::VertexAttribute { 74 | format: wgpu::VertexFormat::Float32x2, 75 | offset: 0, 76 | shader_location: 0, 77 | }], 78 | }; 79 | 80 | // Create uniform buffer 81 | let matrix = Mat4::identity(); 82 | let transform_bytes = matrix.as_byte_slice(); 83 | let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 84 | label: Some("pixels_scaling_renderer_matrix_uniform_buffer"), 85 | contents: transform_bytes, 86 | usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 87 | }); 88 | 89 | // Create bind group 90 | let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 91 | label: Some("pixels_scaling_renderer_bind_group_layout"), 92 | entries: &[ 93 | wgpu::BindGroupLayoutEntry { 94 | binding: 0, 95 | visibility: wgpu::ShaderStages::FRAGMENT, 96 | ty: wgpu::BindingType::Texture { 97 | sample_type: wgpu::TextureSampleType::Float { filterable: true }, 98 | multisampled: false, 99 | view_dimension: wgpu::TextureViewDimension::D2, 100 | }, 101 | count: None, 102 | }, 103 | wgpu::BindGroupLayoutEntry { 104 | binding: 1, 105 | visibility: wgpu::ShaderStages::FRAGMENT, 106 | ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), 107 | count: None, 108 | }, 109 | wgpu::BindGroupLayoutEntry { 110 | binding: 2, 111 | visibility: wgpu::ShaderStages::VERTEX, 112 | ty: wgpu::BindingType::Buffer { 113 | ty: wgpu::BufferBindingType::Uniform, 114 | has_dynamic_offset: false, 115 | min_binding_size: None, // TODO: More efficient to specify this 116 | }, 117 | count: None, 118 | }, 119 | ], 120 | }); 121 | let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 122 | label: Some("pixels_scaling_renderer_bind_group"), 123 | layout: &bind_group_layout, 124 | entries: &[ 125 | wgpu::BindGroupEntry { 126 | binding: 0, 127 | resource: wgpu::BindingResource::TextureView(texture_view), 128 | }, 129 | wgpu::BindGroupEntry { 130 | binding: 1, 131 | resource: wgpu::BindingResource::Sampler(&sampler), 132 | }, 133 | wgpu::BindGroupEntry { 134 | binding: 2, 135 | resource: uniform_buffer.as_entire_binding(), 136 | }, 137 | ], 138 | }); 139 | 140 | // Create pipeline 141 | let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 142 | label: Some("pixels_scaling_renderer_pipeline_layout"), 143 | bind_group_layouts: &[&bind_group_layout], 144 | push_constant_ranges: &[], 145 | }); 146 | let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 147 | label: Some("pixels_scaling_renderer_pipeline"), 148 | layout: Some(&pipeline_layout), 149 | vertex: wgpu::VertexState { 150 | module: &module, 151 | entry_point: "vs_main", 152 | buffers: &[vertex_buffer_layout], 153 | }, 154 | primitive: wgpu::PrimitiveState::default(), 155 | depth_stencil: None, 156 | multisample: wgpu::MultisampleState::default(), 157 | fragment: Some(wgpu::FragmentState { 158 | module: &module, 159 | entry_point: "fs_main", 160 | targets: &[Some(wgpu::ColorTargetState { 161 | format: render_texture_format, 162 | blend: Some(blend_state), 163 | write_mask: wgpu::ColorWrites::ALL, 164 | })], 165 | }), 166 | multiview: None, 167 | }); 168 | 169 | Self { 170 | vertex_buffer, 171 | uniform_buffer, 172 | bind_group, 173 | render_pipeline, 174 | clear_color, 175 | } 176 | } 177 | 178 | /// Draw the pixel buffer to the render target. 179 | pub fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) { 180 | let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 181 | label: Some("pixels_scaling_renderer_render_pass"), 182 | color_attachments: &[Some(wgpu::RenderPassColorAttachment { 183 | view: render_target, 184 | resolve_target: None, 185 | ops: wgpu::Operations { 186 | load: wgpu::LoadOp::Clear(self.clear_color), 187 | store: true, 188 | }, 189 | })], 190 | depth_stencil_attachment: None, 191 | }); 192 | rpass.set_pipeline(&self.render_pipeline); 193 | rpass.set_bind_group(0, &self.bind_group, &[]); 194 | rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); 195 | // todo: add back scissor region for better performance? 196 | rpass.draw(0..6, 0..1); 197 | } 198 | 199 | pub fn update_transform_matrix(&mut self, queue: &wgpu::Queue, matrix: Mat4) { 200 | queue.write_buffer(&self.uniform_buffer, 0, matrix.as_byte_slice()); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /player/src/texture_update_by_coords.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Cursor, Read, Seek, SeekFrom, Write}, 3 | num::{NonZeroU32, NonZeroU64}, 4 | sync::mpsc, 5 | time::Duration, 6 | vec, 7 | }; 8 | 9 | use archive::structures::{Meta, StoredTilePlacement}; 10 | use num::integer::lcm; 11 | use wgpu::{util::DeviceExt, COPY_BUFFER_ALIGNMENT}; 12 | 13 | #[derive(Debug)] 14 | pub enum PartialUpdateResult { 15 | ReachedEndOfInput, 16 | UpdatedUpToMs { 17 | max_ms_since_epoch_used: u32, 18 | // todo: rename? 19 | did_update_up_to_requested_ms: bool, 20 | }, 21 | } 22 | 23 | struct Helpers {} 24 | 25 | impl Helpers { 26 | /// Returns the maximum number of tiles that can be processed in a single chunk. 27 | /// Not guaranteed to meet alignment requirements. 28 | fn get_max_num_of_tiles_per_chunk(device: &wgpu::Device) -> u32 { 29 | device.limits().max_compute_workgroups_per_dimension * NUM_OF_TILES_PER_WORKGROUP 30 | } 31 | 32 | fn get_max_input_size(device: &wgpu::Device) -> u64 { 33 | let absolute_max_in_bytes = device.limits().max_buffer_size.min( 34 | Helpers::get_max_num_of_tiles_per_chunk(device) as u64 35 | * StoredTilePlacement::encoded_size() as u64, 36 | ); 37 | 38 | let max_in_bytes = 39 | absolute_max_in_bytes - (absolute_max_in_bytes % Helpers::get_alignment_factor()); 40 | 41 | max_in_bytes 42 | } 43 | 44 | fn get_aligned_input_size(device: &wgpu::Device, min_size_in_bytes: u64) -> u64 { 45 | let min_in_bytes = min_size_in_bytes 46 | + (Helpers::get_alignment_factor() 47 | - (min_size_in_bytes % Helpers::get_alignment_factor())); 48 | 49 | min_in_bytes.min(Helpers::get_max_input_size(device)) 50 | } 51 | 52 | fn get_alignment_factor() -> u64 { 53 | lcm( 54 | StoredTilePlacement::encoded_size() as u64 * NUM_OF_TILES_PER_WORKGROUP as u64, 55 | COPY_BUFFER_ALIGNMENT as u64, 56 | ) 57 | } 58 | } 59 | 60 | #[derive(Debug)] 61 | struct ComputedBounds { 62 | requested_up_to_ms_since_epoch: u32, 63 | max_ms_since_epoch_seen: u32, 64 | max_ms_since_epoch_used: u32, 65 | max_index_in_chunk_used: u32, 66 | } 67 | 68 | pub struct TextureUpdateByCoords { 69 | reader: R, 70 | meta: Meta, 71 | texture: wgpu::Texture, 72 | texture_extent: wgpu::Extent3d, 73 | pub texture_view: wgpu::TextureView, 74 | bounds_buffer: wgpu::Buffer, 75 | input_buffer: wgpu::Buffer, 76 | zeros_buffer: wgpu::Buffer, 77 | calculate_final_tiles_pipeline: wgpu::ComputePipeline, 78 | calculate_final_tiles_bind_group: wgpu::BindGroup, 79 | update_texture_pipeline: wgpu::ComputePipeline, 80 | update_texture_bind_group: wgpu::BindGroup, 81 | last_index_for_tile: wgpu::Buffer, 82 | staging_buffer: wgpu::Buffer, 83 | staging_belt: wgpu::util::StagingBelt, 84 | } 85 | 86 | const NUM_OF_TILES_PER_WORKGROUP: u32 = 4; 87 | 88 | // todo: add note about assuming sorted input 89 | 90 | impl TextureUpdateByCoords { 91 | pub fn new( 92 | device: &wgpu::Device, 93 | meta: Meta, 94 | reader: R, 95 | texture_usages: Option, 96 | ) -> Self { 97 | let shader = wgpu::include_wgsl!("../shaders/texture_update_by_coords.compute.wgsl"); 98 | let module = device.create_shader_module(shader); 99 | 100 | let calculate_final_tiles_pipeline = 101 | device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { 102 | label: Some("texture_update_by_coords calculate_final_tiles_pipeline"), 103 | layout: None, 104 | module: &module, 105 | entry_point: "calculate_final_tiles", 106 | }); 107 | 108 | let update_texture_pipeline = 109 | device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { 110 | label: Some("texture_update_by_coords update_texture_pipeline"), 111 | layout: None, 112 | module: &module, 113 | entry_point: "update_texture", 114 | }); 115 | 116 | let bounds_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 117 | label: Some("texture_update_by_coords bounds buffer"), 118 | contents: bytemuck::cast_slice(&[0u32; 4]), 119 | usage: wgpu::BufferUsages::STORAGE 120 | | wgpu::BufferUsages::COPY_DST 121 | | wgpu::BufferUsages::COPY_SRC, 122 | }); 123 | 124 | let staging_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 125 | label: Some("texture_update_by_coords staging buffer"), 126 | contents: bytemuck::cast_slice(&[0u32; 4]), 127 | usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, 128 | }); 129 | 130 | let input_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 131 | label: Some("texture_update_by_coords input buffer"), 132 | contents: bytemuck::cast_slice(&vec![ 133 | 0u8; 134 | Helpers::get_max_input_size(device) 135 | .try_into() 136 | .unwrap() 137 | ]), 138 | usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, 139 | }); 140 | 141 | let mut r: Vec<[u32; 4]> = meta 142 | .clone() 143 | .color_id_to_tuple 144 | .into_values() 145 | .map(|x| [x[0] as u32, x[1] as u32, x[2] as u32, x[3] as u32]) 146 | .collect(); 147 | 148 | // Pad to 256 color tuples 149 | while r.len() < 256 { 150 | r.push([0, 0, 0, 0]); 151 | } 152 | 153 | let size = meta.get_largest_canvas_size().unwrap(); 154 | 155 | let mut r = r.into_iter().flatten().collect::>(); 156 | // Padding for alignment 157 | r.append(&mut vec![size.width.into(), size.height.into()]); 158 | r.append(&mut vec![0; 2]); 159 | 160 | let locals_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 161 | label: Some("texture_update_by_coords locals buffer"), 162 | contents: bytemuck::cast_slice(&r), 163 | usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 164 | }); 165 | 166 | let texture_extent = wgpu::Extent3d { 167 | width: size.width.into(), 168 | height: size.height.into(), 169 | depth_or_array_layers: 1, 170 | }; 171 | 172 | let texture_desc = wgpu::TextureDescriptor { 173 | size: texture_extent, 174 | mip_level_count: 1, 175 | sample_count: 1, 176 | dimension: wgpu::TextureDimension::D2, 177 | view_formats: &[wgpu::TextureFormat::Rgba8Unorm], 178 | format: wgpu::TextureFormat::Rgba8Unorm, 179 | usage: wgpu::TextureUsages::TEXTURE_BINDING 180 | | wgpu::TextureUsages::STORAGE_BINDING 181 | | wgpu::TextureUsages::RENDER_ATTACHMENT 182 | | texture_usages.unwrap_or(wgpu::TextureUsages::empty()), 183 | label: None, 184 | }; 185 | let texture = device.create_texture(&texture_desc); 186 | 187 | let some_view = texture.create_view(&wgpu::TextureViewDescriptor { 188 | label: None, 189 | format: Some(wgpu::TextureFormat::Rgba8Unorm), 190 | base_mip_level: 0, 191 | mip_level_count: NonZeroU32::new(1), 192 | ..Default::default() 193 | }); 194 | 195 | let z = vec![0u32; size.width as usize * size.height as usize]; 196 | 197 | let last_index_for_tile = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 198 | label: Some("texture_update_by_coords last index buffer"), 199 | contents: bytemuck::cast_slice(&z), 200 | usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, 201 | }); 202 | 203 | let zeros_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 204 | label: Some("texture_update_by_coords zeros buffer buffer"), 205 | contents: bytemuck::cast_slice(&z), 206 | usage: wgpu::BufferUsages::COPY_SRC, 207 | }); 208 | 209 | let calculate_final_tiles_bind_group_layout = 210 | calculate_final_tiles_pipeline.get_bind_group_layout(0); 211 | let calculate_final_tiles_bind_group = 212 | device.create_bind_group(&wgpu::BindGroupDescriptor { 213 | label: None, 214 | layout: &calculate_final_tiles_bind_group_layout, 215 | entries: &[ 216 | wgpu::BindGroupEntry { 217 | binding: 0, 218 | resource: input_buffer.as_entire_binding(), 219 | }, 220 | wgpu::BindGroupEntry { 221 | binding: 1, 222 | resource: locals_buffer.as_entire_binding(), 223 | }, 224 | wgpu::BindGroupEntry { 225 | binding: 2, 226 | resource: last_index_for_tile.as_entire_binding(), 227 | }, 228 | wgpu::BindGroupEntry { 229 | binding: 3, 230 | resource: bounds_buffer.as_entire_binding(), 231 | }, 232 | ], 233 | }); 234 | 235 | let update_texture_bind_group_layout = update_texture_pipeline.get_bind_group_layout(0); 236 | let update_texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 237 | label: None, 238 | layout: &update_texture_bind_group_layout, 239 | entries: &[ 240 | wgpu::BindGroupEntry { 241 | binding: 0, 242 | resource: input_buffer.as_entire_binding(), 243 | }, 244 | wgpu::BindGroupEntry { 245 | binding: 1, 246 | resource: locals_buffer.as_entire_binding(), 247 | }, 248 | wgpu::BindGroupEntry { 249 | binding: 2, 250 | resource: last_index_for_tile.as_entire_binding(), 251 | }, 252 | wgpu::BindGroupEntry { 253 | binding: 3, 254 | resource: bounds_buffer.as_entire_binding(), 255 | }, 256 | wgpu::BindGroupEntry { 257 | binding: 4, 258 | resource: wgpu::BindingResource::TextureView(&some_view), 259 | }, 260 | ], 261 | }); 262 | 263 | Self { 264 | reader, 265 | meta, 266 | bounds_buffer, 267 | input_buffer, 268 | texture, 269 | texture_extent, 270 | texture_view: some_view, 271 | calculate_final_tiles_pipeline, 272 | calculate_final_tiles_bind_group, 273 | update_texture_pipeline, 274 | update_texture_bind_group, 275 | zeros_buffer, 276 | last_index_for_tile, 277 | staging_buffer, 278 | // todo: use correct chunk size 279 | staging_belt: wgpu::util::StagingBelt::new(Helpers::get_max_input_size(device) as u64), 280 | } 281 | } 282 | 283 | /// Make sure to only pass one tile per position, as it's not guaranteed that the order of tiles will be preserved during rendering. 284 | /// todo: add note about calling only once per frame 285 | /// `duration` is used as a performance hint. 286 | pub fn update( 287 | &mut self, 288 | device: &wgpu::Device, 289 | queue: &wgpu::Queue, 290 | up_to_ms: u32, 291 | duration: Duration, 292 | ) -> PartialUpdateResult { 293 | loop { 294 | match pollster::block_on(self.partial_update(device, queue, up_to_ms, duration)) { 295 | PartialUpdateResult::ReachedEndOfInput => { 296 | return PartialUpdateResult::ReachedEndOfInput; 297 | } 298 | PartialUpdateResult::UpdatedUpToMs { 299 | max_ms_since_epoch_used, 300 | did_update_up_to_requested_ms, 301 | } => { 302 | if did_update_up_to_requested_ms { 303 | return PartialUpdateResult::UpdatedUpToMs { 304 | max_ms_since_epoch_used, 305 | did_update_up_to_requested_ms, 306 | }; 307 | } 308 | } 309 | } 310 | } 311 | } 312 | 313 | async fn partial_update( 314 | &mut self, 315 | device: &wgpu::Device, 316 | queue: &wgpu::Queue, 317 | up_to_ms: u32, 318 | duration: Duration, 319 | ) -> PartialUpdateResult { 320 | let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { 321 | label: Some("texture_update_by_coords encoder"), 322 | }); 323 | 324 | { 325 | let mut bounds_mut = self.staging_belt.write_buffer( 326 | &mut encoder, 327 | &self.bounds_buffer, 328 | 0, 329 | NonZeroU64::new(self.bounds_buffer.size()).unwrap(), 330 | device, 331 | ); 332 | bytemuck::cast_slice_mut::(&mut bounds_mut)[0] = up_to_ms; 333 | bytemuck::cast_slice_mut::(&mut bounds_mut)[1] = 0; 334 | bytemuck::cast_slice_mut::(&mut bounds_mut)[2] = 0; 335 | bytemuck::cast_slice_mut::(&mut bounds_mut)[3] = 0; 336 | } 337 | 338 | let bytes_written = self 339 | .write_next_input_chunk(&mut encoder, device, duration) 340 | .unwrap(); 341 | self.staging_belt.finish(); 342 | 343 | if bytes_written == 0 { 344 | queue.submit(Some(encoder.finish())); 345 | return PartialUpdateResult::ReachedEndOfInput; 346 | } 347 | 348 | let num_of_tiles = bytes_written / StoredTilePlacement::encoded_size(); 349 | 350 | let num_of_workgroups = 351 | f32::ceil(num_of_tiles as f32 / NUM_OF_TILES_PER_WORKGROUP as f32) as u32; 352 | 353 | { 354 | let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { 355 | label: Some("texture_update_by_coords.calculate_final_tiles compute pass"), 356 | }); 357 | cpass.set_pipeline(&self.calculate_final_tiles_pipeline); 358 | cpass.set_bind_group(0, &self.calculate_final_tiles_bind_group, &[]); 359 | 360 | cpass.dispatch_workgroups(num_of_workgroups, NUM_OF_TILES_PER_WORKGROUP, 1); 361 | } 362 | 363 | { 364 | let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { 365 | label: Some("texture_update_by_coords.update_texture compute pass"), 366 | }); 367 | cpass.set_pipeline(&self.update_texture_pipeline); 368 | cpass.set_bind_group(0, &self.update_texture_bind_group, &[]); 369 | 370 | cpass.dispatch_workgroups(num_of_workgroups, NUM_OF_TILES_PER_WORKGROUP, 1); 371 | } 372 | 373 | // Clear state data in preparation for next chunk 374 | encoder.copy_buffer_to_buffer( 375 | &self.zeros_buffer, 376 | 0, 377 | &self.last_index_for_tile, 378 | 0, 379 | self.last_index_for_tile.size(), 380 | ); 381 | 382 | encoder.copy_buffer_to_buffer( 383 | &self.bounds_buffer, 384 | 0, 385 | &self.staging_buffer, 386 | 0, 387 | self.bounds_buffer.size(), 388 | ); 389 | 390 | queue.submit(Some(encoder.finish())); 391 | self.staging_belt.recall(); 392 | 393 | let bounds = self.read_computed_bounds(&device).await; 394 | 395 | if bounds.max_index_in_chunk_used != (num_of_tiles as u32 - 1) { 396 | self.reader 397 | .seek(SeekFrom::Current( 398 | -((num_of_tiles as i64 - bounds.max_index_in_chunk_used as i64) 399 | * StoredTilePlacement::encoded_size() as i64), 400 | )) 401 | .unwrap(); 402 | } 403 | 404 | return PartialUpdateResult::UpdatedUpToMs { 405 | max_ms_since_epoch_used: bounds.max_ms_since_epoch_used, 406 | did_update_up_to_requested_ms: bounds.max_ms_since_epoch_seen >= up_to_ms, 407 | }; 408 | } 409 | 410 | fn write_next_input_chunk( 411 | &mut self, 412 | encoder: &mut wgpu::CommandEncoder, 413 | device: &wgpu::Device, 414 | duration: Duration, 415 | ) -> std::io::Result { 416 | let estimated_num_of_tiles = self.get_estimated_num_of_tiles_for_duration(duration); 417 | let copy_size = Helpers::get_aligned_input_size( 418 | device, 419 | estimated_num_of_tiles 420 | .checked_mul(StoredTilePlacement::encoded_size() as u64) 421 | .unwrap_or(Helpers::get_max_input_size(device)), 422 | ); 423 | 424 | let mut s = self.staging_belt.write_buffer( 425 | encoder, 426 | &self.input_buffer, 427 | 0, 428 | NonZeroU64::new(copy_size).unwrap(), 429 | device, 430 | ); 431 | 432 | let mut tracked_writer = Cursor::new(s.as_mut()); 433 | 434 | // todo: handle errors 435 | match std::io::copy( 436 | &mut self.reader.by_ref().take(copy_size), 437 | &mut tracked_writer, 438 | ) { 439 | Ok(num_written) => { 440 | if num_written == 0 { 441 | return Ok(0); 442 | } 443 | 444 | let mut padding_count = 0; 445 | // Pad 446 | while (num_written + padding_count) 447 | % ((NUM_OF_TILES_PER_WORKGROUP as usize) * StoredTilePlacement::encoded_size()) 448 | as u64 449 | != 0 450 | { 451 | StoredTilePlacement { 452 | x: 0, 453 | y: 0, 454 | color_index: 255, 455 | ms_since_epoch: 0, 456 | } 457 | .write_into(&mut tracked_writer); 458 | 459 | padding_count += StoredTilePlacement::encoded_size() as u64; 460 | } 461 | 462 | Ok(num_written as usize) 463 | } 464 | Err(err) => Err(err), 465 | } 466 | } 467 | 468 | async fn read_computed_bounds(&self, device: &wgpu::Device) -> ComputedBounds { 469 | let buffer_slice = self.staging_buffer.slice(..); 470 | let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); 471 | buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap()); 472 | 473 | device.poll(wgpu::Maintain::Wait); 474 | 475 | receiver.receive().await.unwrap().unwrap(); 476 | 477 | let data = buffer_slice.get_mapped_range(); 478 | let cast_data = bytemuck::cast_slice::(&data).to_vec(); 479 | 480 | let bounds = ComputedBounds { 481 | requested_up_to_ms_since_epoch: cast_data[0], 482 | max_ms_since_epoch_seen: cast_data[1], 483 | max_ms_since_epoch_used: cast_data[2], 484 | max_index_in_chunk_used: cast_data[3], 485 | }; 486 | 487 | drop(data); 488 | self.staging_buffer.unmap(); 489 | 490 | return bounds; 491 | } 492 | 493 | fn get_estimated_num_of_tiles_for_duration(&self, duration: Duration) -> u64 { 494 | let average_tiles_placed_per_ms = self.meta.total_tile_placements as f64 495 | // Add 1 to prevent division by 0 496 | / (self.meta.last_tile_placed_at_ms_since_epoch as f64 + 1.0); 497 | 498 | let estimated_num_of_tiles = 499 | (average_tiles_placed_per_ms * duration.as_millis() as f64) as u64; 500 | 501 | return estimated_num_of_tiles; 502 | } 503 | } 504 | 505 | #[cfg(test)] 506 | mod tests { 507 | use archive::structures::{CanvasSizeChange, Meta, StoredTilePlacement}; 508 | use image::{ImageBuffer, Rgba}; 509 | use log::{log_enabled, Level}; 510 | use rand::Rng; 511 | use std::{ 512 | collections::BTreeMap, 513 | io::Cursor, 514 | num::NonZeroU32, 515 | sync::mpsc::{self}, 516 | time::Duration, 517 | }; 518 | use wgpu::{Device, COPY_BYTES_PER_ROW_ALIGNMENT}; 519 | 520 | use crate::texture_update_by_coords::PartialUpdateResult; 521 | 522 | use super::TextureUpdateByCoords; 523 | 524 | struct TestHelpers {} 525 | 526 | impl TestHelpers { 527 | pub fn render_to_buffer( 528 | test_name: &str, 529 | meta: Meta, 530 | data: Vec, 531 | up_to_ms: u32, 532 | ) -> ImageBuffer, Vec> { 533 | let (device, queue) = Self::get_device(); 534 | 535 | let mut controller = TextureUpdateByCoords::new( 536 | &device, 537 | meta.clone(), 538 | Cursor::new(data), 539 | Some(wgpu::TextureUsages::COPY_SRC), 540 | ); 541 | controller.update(&device, &queue, up_to_ms, Duration::from_secs(100)); 542 | 543 | let buffer = Self::texture_to_buffer( 544 | &device, 545 | &queue, 546 | &controller.texture, 547 | controller.texture_extent, 548 | ); 549 | Self::save_debug_image(test_name, &buffer); 550 | buffer 551 | } 552 | 553 | pub fn save_debug_image(test_name: &str, buffer: &ImageBuffer, Vec>) { 554 | env_logger::try_init().ok(); 555 | 556 | if log_enabled!(Level::Debug) { 557 | buffer.save(format!("{}.png", test_name)).unwrap(); 558 | } 559 | } 560 | 561 | pub fn texture_to_buffer( 562 | device: &Device, 563 | queue: &wgpu::Queue, 564 | texture: &wgpu::Texture, 565 | texture_extent: wgpu::Extent3d, 566 | ) -> ImageBuffer, Vec> { 567 | let u32_size = std::mem::size_of::() as u32; 568 | let output_buffer_size = (u32_size * texture_extent.width * texture_extent.height * 8) 569 | as wgpu::BufferAddress; 570 | let output_buffer_desc = wgpu::BufferDescriptor { 571 | size: output_buffer_size, 572 | usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, 573 | label: None, 574 | mapped_at_creation: false, 575 | }; 576 | let output_buffer = device.create_buffer(&output_buffer_desc); 577 | 578 | let mut encoder = 579 | device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); 580 | 581 | let bytes_per_row = (u32_size * texture_extent.width) 582 | + (COPY_BYTES_PER_ROW_ALIGNMENT - 1) 583 | & !(COPY_BYTES_PER_ROW_ALIGNMENT - 1); 584 | 585 | encoder.copy_texture_to_buffer( 586 | wgpu::ImageCopyTexture { 587 | aspect: wgpu::TextureAspect::All, 588 | texture: &texture, 589 | mip_level: 0, 590 | origin: wgpu::Origin3d::ZERO, 591 | }, 592 | wgpu::ImageCopyBuffer { 593 | buffer: &output_buffer, 594 | layout: wgpu::ImageDataLayout { 595 | offset: 0, 596 | bytes_per_row: NonZeroU32::new(bytes_per_row), 597 | rows_per_image: NonZeroU32::new(texture_extent.height), 598 | }, 599 | }, 600 | texture_extent, 601 | ); 602 | 603 | queue.submit(Some(encoder.finish())); 604 | 605 | let buffer_slice = output_buffer.slice(..); 606 | 607 | let (tx, rx) = mpsc::channel(); 608 | buffer_slice.map_async(wgpu::MapMode::Read, move |result| { 609 | tx.send(result).unwrap(); 610 | }); 611 | device.poll(wgpu::Maintain::Wait); 612 | rx.recv().unwrap().unwrap(); 613 | 614 | let mut data = buffer_slice.get_mapped_range().to_vec(); 615 | 616 | // Repack buffer if bytes_per_row is not equal to width 617 | if bytes_per_row != texture_extent.width * u32_size { 618 | let mut repacked_data = Vec::with_capacity( 619 | (texture_extent.width * texture_extent.height * u32_size) as usize, 620 | ); 621 | for row in 0..texture_extent.height { 622 | let row_start = (row * bytes_per_row) as usize; 623 | let row_end = row_start + (texture_extent.width * u32_size) as usize; 624 | repacked_data.extend_from_slice(&data[row_start..row_end]); 625 | } 626 | data = repacked_data; 627 | } 628 | 629 | let buffer = ImageBuffer::, _>::from_raw( 630 | texture_extent.width, 631 | texture_extent.height, 632 | // copy data to avoid dealing with lifetimes 633 | data.to_vec(), 634 | ) 635 | .unwrap(); 636 | 637 | buffer 638 | } 639 | 640 | pub fn get_device() -> (Device, wgpu::Queue) { 641 | pollster::block_on(Self::get_device_async()) 642 | } 643 | async fn get_device_async() -> (Device, wgpu::Queue) { 644 | let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default()); 645 | 646 | let adapter = instance 647 | .request_adapter(&wgpu::RequestAdapterOptions { 648 | power_preference: wgpu::PowerPreference::LowPower, 649 | compatible_surface: None, 650 | force_fallback_adapter: false, 651 | }) 652 | .await 653 | .unwrap(); 654 | 655 | adapter 656 | .request_device( 657 | &wgpu::DeviceDescriptor { 658 | features: wgpu::Features::empty(), 659 | limits: wgpu::Limits::default(), 660 | label: None, 661 | }, 662 | None, 663 | ) 664 | .await 665 | .unwrap() 666 | } 667 | } 668 | 669 | #[test] 670 | fn black_rows() { 671 | let mut color_id_to_tuple = BTreeMap::new(); 672 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 673 | 674 | let texture_size: u32 = 65; 675 | 676 | let mut data: Vec = Vec::new(); 677 | 678 | // Every other row with black 679 | for x in 0..texture_size { 680 | for y in 0..texture_size { 681 | if y % 2 == 0 { 682 | continue; 683 | } 684 | 685 | let tile = StoredTilePlacement { 686 | x: x as u16, 687 | y: y as u16, 688 | color_index: 0, 689 | ms_since_epoch: 0, 690 | }; 691 | 692 | tile.write_into(&mut data); 693 | } 694 | } 695 | 696 | let meta = Meta { 697 | chunk_descs: vec![], 698 | color_id_to_tuple, 699 | last_tile_placed_at_ms_since_epoch: 0, 700 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 701 | canvas_size_changes: vec![CanvasSizeChange { 702 | width: texture_size as u16, 703 | height: texture_size as u16, 704 | ms_since_epoch: 0, 705 | }], 706 | }; 707 | 708 | let buffer = TestHelpers::render_to_buffer("black_rows", meta, data, 0); 709 | 710 | // Check generated texture 711 | for x in 0..texture_size { 712 | for y in 0..texture_size { 713 | if y % 2 == 0 { 714 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 715 | } else { 716 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 717 | } 718 | } 719 | } 720 | } 721 | 722 | #[test] 723 | fn red_square() { 724 | let mut color_id_to_tuple = BTreeMap::new(); 725 | color_id_to_tuple.insert(0, [255, 0, 0, 255]); 726 | 727 | let texture_size: u32 = 64; 728 | 729 | let mut data: Vec = Vec::new(); 730 | 731 | // Fill with red 732 | for x in 0..texture_size { 733 | for y in 0..texture_size { 734 | let tile = StoredTilePlacement { 735 | x: x as u16, 736 | y: y as u16, 737 | color_index: 0, 738 | ms_since_epoch: 0, 739 | }; 740 | 741 | tile.write_into(&mut data); 742 | } 743 | } 744 | 745 | let meta = Meta { 746 | chunk_descs: vec![], 747 | color_id_to_tuple, 748 | last_tile_placed_at_ms_since_epoch: 0, 749 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 750 | canvas_size_changes: vec![CanvasSizeChange { 751 | width: texture_size as u16, 752 | height: texture_size as u16, 753 | ms_since_epoch: 0, 754 | }], 755 | }; 756 | 757 | let buffer = TestHelpers::render_to_buffer("red_square", meta, data, 0); 758 | 759 | // Check generated texture 760 | for x in 0..texture_size { 761 | for y in 0..texture_size { 762 | assert_eq!(buffer.get_pixel(x, y), &Rgba([255, 0, 0, 255])); 763 | } 764 | } 765 | } 766 | 767 | #[test] 768 | fn ignores_future_tile_placements() { 769 | let mut color_id_to_tuple = BTreeMap::new(); 770 | color_id_to_tuple.insert(0, [255, 0, 0, 255]); 771 | color_id_to_tuple.insert(1, [0, 255, 0, 255]); 772 | 773 | let texture_size: u32 = 64; 774 | 775 | let mut data: Vec = Vec::new(); 776 | 777 | // Fill with red 778 | for x in 0..texture_size { 779 | for y in 0..texture_size { 780 | let tile = StoredTilePlacement { 781 | x: x as u16, 782 | y: y as u16, 783 | color_index: 0, 784 | ms_since_epoch: 1, 785 | }; 786 | 787 | tile.write_into(&mut data); 788 | } 789 | } 790 | 791 | // Fill with blue in the future 792 | for x in 0..texture_size { 793 | for y in 0..texture_size { 794 | let tile = StoredTilePlacement { 795 | x: x as u16, 796 | y: y as u16, 797 | color_index: 1, 798 | ms_since_epoch: 2, 799 | }; 800 | 801 | tile.write_into(&mut data); 802 | } 803 | } 804 | 805 | let meta = Meta { 806 | chunk_descs: vec![], 807 | color_id_to_tuple, 808 | last_tile_placed_at_ms_since_epoch: 2, 809 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 810 | canvas_size_changes: vec![CanvasSizeChange { 811 | width: texture_size as u16, 812 | height: texture_size as u16, 813 | ms_since_epoch: 0, 814 | }], 815 | }; 816 | 817 | let buffer = TestHelpers::render_to_buffer("ignores_future_tile_placements", meta, data, 1); 818 | 819 | // Check generated texture 820 | for x in 0..texture_size { 821 | for y in 0..texture_size { 822 | assert_eq!(buffer.get_pixel(x, y), &Rgba([255, 0, 0, 255])); 823 | } 824 | } 825 | } 826 | 827 | #[test] 828 | fn single_pixel() { 829 | let mut color_id_to_tuple = BTreeMap::new(); 830 | color_id_to_tuple.insert(0, [255, 0, 0, 255]); 831 | 832 | let texture_size: u32 = 64; 833 | 834 | let mut data: Vec = Vec::new(); 835 | 836 | StoredTilePlacement { 837 | x: 63, 838 | y: 63, 839 | color_index: 0, 840 | ms_since_epoch: 0, 841 | } 842 | .write_into(&mut data); 843 | 844 | let meta = Meta { 845 | chunk_descs: vec![], 846 | color_id_to_tuple, 847 | last_tile_placed_at_ms_since_epoch: 0, 848 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 849 | canvas_size_changes: vec![CanvasSizeChange { 850 | width: texture_size as u16, 851 | height: texture_size as u16, 852 | ms_since_epoch: 0, 853 | }], 854 | }; 855 | 856 | let buffer = TestHelpers::render_to_buffer("single_pixel", meta, data, 0); 857 | 858 | // Check generated texture 859 | for x in 0..texture_size { 860 | for y in 0..texture_size { 861 | if x == 63 && y == 63 { 862 | assert_eq!(buffer.get_pixel(x, y), &Rgba([255, 0, 0, 255])); 863 | } else { 864 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 865 | } 866 | } 867 | } 868 | } 869 | 870 | #[test] 871 | fn multi_color() { 872 | let mut color_id_to_tuple = BTreeMap::new(); 873 | color_id_to_tuple.insert(0, [255, 0, 0, 255]); 874 | color_id_to_tuple.insert(1, [0, 255, 0, 255]); 875 | color_id_to_tuple.insert(2, [0, 0, 255, 255]); 876 | 877 | let texture_size: u32 = 64; 878 | 879 | let mut data: Vec = Vec::new(); 880 | 881 | for x in 0..texture_size { 882 | for y in 0..texture_size { 883 | let tile = StoredTilePlacement { 884 | x: x as u16, 885 | y: y as u16, 886 | color_index: (x % 3) as u8, 887 | ms_since_epoch: 0, 888 | }; 889 | 890 | tile.write_into(&mut data); 891 | } 892 | } 893 | 894 | let meta = Meta { 895 | chunk_descs: vec![], 896 | color_id_to_tuple, 897 | last_tile_placed_at_ms_since_epoch: 0, 898 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 899 | canvas_size_changes: vec![CanvasSizeChange { 900 | width: texture_size as u16, 901 | height: texture_size as u16, 902 | ms_since_epoch: 0, 903 | }], 904 | }; 905 | 906 | let buffer = TestHelpers::render_to_buffer("multi_color", meta, data, 0); 907 | 908 | // Check generated texture 909 | for x in 0..texture_size { 910 | for y in 0..texture_size { 911 | let expected_color = match x % 3 { 912 | 0 => [255, 0, 0, 255], 913 | 1 => [0, 255, 0, 255], 914 | 2 => [0, 0, 255, 255], 915 | _ => unreachable!(), 916 | }; 917 | 918 | assert_eq!(buffer.get_pixel(x, y), &Rgba(expected_color)); 919 | } 920 | } 921 | } 922 | 923 | #[test] 924 | fn odd_number_of_tiles() { 925 | let mut color_id_to_tuple = BTreeMap::new(); 926 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 927 | 928 | let texture_size: u32 = 64; 929 | 930 | let mut data: Vec = Vec::new(); 931 | 932 | for i in 0..7 { 933 | StoredTilePlacement { 934 | x: i as u16, 935 | y: i as u16, 936 | color_index: 0, 937 | ms_since_epoch: 0, 938 | } 939 | .write_into(&mut data); 940 | } 941 | 942 | let meta = Meta { 943 | chunk_descs: vec![], 944 | color_id_to_tuple, 945 | last_tile_placed_at_ms_since_epoch: 0, 946 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 947 | canvas_size_changes: vec![CanvasSizeChange { 948 | width: texture_size as u16, 949 | height: texture_size as u16, 950 | ms_since_epoch: 0, 951 | }], 952 | }; 953 | 954 | let buffer = TestHelpers::render_to_buffer("odd_number_of_tiles", meta, data, 0); 955 | 956 | // Check generated texture 957 | for x in 0..texture_size { 958 | for y in 0..texture_size { 959 | if x < 7 && y < 7 && x == y { 960 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 961 | } else { 962 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 963 | } 964 | } 965 | } 966 | } 967 | 968 | #[test] 969 | fn preserves_order_of_tiles_in_chunk() { 970 | let mut color_id_to_tuple = BTreeMap::new(); 971 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 972 | color_id_to_tuple.insert(1, [255, 0, 0, 255]); 973 | 974 | let texture_size: u32 = 64; 975 | 976 | let mut data: Vec = Vec::new(); 977 | 978 | for x in 0..texture_size { 979 | for y in 0..texture_size { 980 | StoredTilePlacement { 981 | x: x as u16, 982 | y: y as u16, 983 | color_index: 0, 984 | ms_since_epoch: 0, 985 | } 986 | .write_into(&mut data); 987 | 988 | StoredTilePlacement { 989 | x: x as u16, 990 | y: y as u16, 991 | color_index: 1, 992 | ms_since_epoch: 0, 993 | } 994 | .write_into(&mut data); 995 | } 996 | } 997 | 998 | let meta = Meta { 999 | chunk_descs: vec![], 1000 | color_id_to_tuple, 1001 | last_tile_placed_at_ms_since_epoch: 0, 1002 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 1003 | canvas_size_changes: vec![CanvasSizeChange { 1004 | width: texture_size as u16, 1005 | height: texture_size as u16, 1006 | ms_since_epoch: 0, 1007 | }], 1008 | }; 1009 | 1010 | let buffer = 1011 | TestHelpers::render_to_buffer("preserves_order_of_tiles_in_chunk", meta, data, 0); 1012 | 1013 | // Check generated texture 1014 | for x in 0..texture_size { 1015 | for y in 0..texture_size { 1016 | assert_eq!(buffer.get_pixel(x, y), &Rgba([255, 0, 0, 255])); 1017 | } 1018 | } 1019 | } 1020 | 1021 | #[test] 1022 | // todo: fix test with new dynamic chunking 1023 | fn multiple_chunks() { 1024 | let mut color_id_to_tuple = BTreeMap::new(); 1025 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 1026 | color_id_to_tuple.insert(1, [255, 0, 0, 255]); 1027 | 1028 | let texture_size: u32 = 64; 1029 | 1030 | let mut expected_color_index = 0; 1031 | 1032 | let required_num_of_tile_updates = 2048 * 2; 1033 | let required_num_of_full_texture_refreshes = 1034 | required_num_of_tile_updates / (texture_size * texture_size); 1035 | 1036 | let mut data: Vec = Vec::new(); 1037 | 1038 | for i in 0..required_num_of_full_texture_refreshes { 1039 | if i % 2 == 0 { 1040 | for x in 0..texture_size { 1041 | for y in 0..texture_size { 1042 | StoredTilePlacement { 1043 | x: x as u16, 1044 | y: y as u16, 1045 | color_index: 0, 1046 | ms_since_epoch: 0, 1047 | } 1048 | .write_into(&mut data); 1049 | } 1050 | } 1051 | 1052 | expected_color_index = 0; 1053 | } else { 1054 | for x in (0..texture_size).rev() { 1055 | for y in (0..texture_size).rev() { 1056 | StoredTilePlacement { 1057 | x: x as u16, 1058 | y: y as u16, 1059 | color_index: 1, 1060 | ms_since_epoch: 0, 1061 | } 1062 | .write_into(&mut data); 1063 | } 1064 | } 1065 | 1066 | expected_color_index = 1; 1067 | } 1068 | } 1069 | 1070 | let meta = Meta { 1071 | chunk_descs: vec![], 1072 | color_id_to_tuple: color_id_to_tuple.clone(), 1073 | last_tile_placed_at_ms_since_epoch: 0, 1074 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 1075 | canvas_size_changes: vec![CanvasSizeChange { 1076 | width: texture_size as u16, 1077 | height: texture_size as u16, 1078 | ms_since_epoch: 0, 1079 | }], 1080 | }; 1081 | 1082 | let buffer = TestHelpers::render_to_buffer("multiple_chunks", meta, data, 0); 1083 | 1084 | // Check generated texture 1085 | let expected_color = Rgba(*color_id_to_tuple.get(&expected_color_index).unwrap()); 1086 | for x in 0..texture_size { 1087 | for y in 0..texture_size { 1088 | assert_eq!(buffer.get_pixel(x, y), &expected_color); 1089 | } 1090 | } 1091 | } 1092 | 1093 | #[test] 1094 | fn fuzz() { 1095 | let mut color_id_to_tuple = BTreeMap::new(); 1096 | 1097 | let mut generator = rand::thread_rng(); 1098 | 1099 | for i in 0..128 { 1100 | color_id_to_tuple.insert( 1101 | i, 1102 | [ 1103 | generator.gen_range(0..255), 1104 | generator.gen_range(0..255), 1105 | generator.gen_range(0..255), 1106 | 255, 1107 | ], 1108 | ); 1109 | } 1110 | 1111 | let texture_size: u32 = 2000; 1112 | 1113 | let mut expected_texture: Vec> = 1114 | vec![vec![0xff; texture_size as usize]; texture_size as usize]; 1115 | 1116 | let mut data = Vec::new(); 1117 | 1118 | for i in 0..100 { 1119 | for _ in 0..texture_size { 1120 | let x = generator.gen_range(0..texture_size); 1121 | let y = generator.gen_range(0..texture_size); 1122 | 1123 | for _ in 0..5 { 1124 | let color_index = generator.gen_range(0..color_id_to_tuple.len()) as u8; 1125 | StoredTilePlacement { 1126 | x: x as u16, 1127 | y: y as u16, 1128 | color_index: color_index, 1129 | ms_since_epoch: i, 1130 | } 1131 | .write_into(&mut data); 1132 | 1133 | expected_texture[x as usize][y as usize] = color_index; 1134 | } 1135 | } 1136 | } 1137 | 1138 | let meta = Meta { 1139 | chunk_descs: vec![], 1140 | color_id_to_tuple: color_id_to_tuple.clone(), 1141 | last_tile_placed_at_ms_since_epoch: 99, 1142 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 1143 | canvas_size_changes: vec![CanvasSizeChange { 1144 | width: texture_size as u16, 1145 | height: texture_size as u16, 1146 | ms_since_epoch: 0, 1147 | }], 1148 | }; 1149 | 1150 | let (device, queue) = TestHelpers::get_device(); 1151 | let mut controller = TextureUpdateByCoords::new( 1152 | &device, 1153 | meta, 1154 | Cursor::new(data), 1155 | Some(wgpu::TextureUsages::COPY_SRC), 1156 | ); 1157 | 1158 | // Reset to clear 1159 | let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { 1160 | label: Some("clear_encoder"), 1161 | }); 1162 | 1163 | { 1164 | encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 1165 | label: Some("Clear render pass"), 1166 | color_attachments: &[Some(wgpu::RenderPassColorAttachment { 1167 | view: &controller.texture_view, 1168 | resolve_target: None, 1169 | ops: wgpu::Operations { 1170 | load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), 1171 | store: true, 1172 | }, 1173 | })], 1174 | depth_stencil_attachment: None, 1175 | }); 1176 | } 1177 | 1178 | queue.submit(Some(encoder.finish())); 1179 | 1180 | // Render tile updates 1181 | controller.update(&device, &queue, 1_000_000_000, Duration::from_secs(1_000)); 1182 | 1183 | let buffer = TestHelpers::texture_to_buffer( 1184 | &device, 1185 | &queue, 1186 | &controller.texture, 1187 | controller.texture_extent, 1188 | ); 1189 | TestHelpers::save_debug_image("fuzz", &buffer); 1190 | 1191 | // Check generated texture 1192 | for x in 0..texture_size { 1193 | for y in 0..texture_size { 1194 | let expected_color_key = expected_texture[x as usize][y as usize]; 1195 | if expected_color_key == 0xff { 1196 | // This tile wasn't updated, so it should be equal to the color we cleared with 1197 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 1198 | continue; 1199 | } 1200 | 1201 | assert_eq!( 1202 | buffer.get_pixel(x, y), 1203 | &Rgba( 1204 | *color_id_to_tuple 1205 | .get(&expected_texture[x as usize][y as usize]) 1206 | .unwrap() 1207 | ) 1208 | ); 1209 | } 1210 | } 1211 | } 1212 | 1213 | #[test] 1214 | fn up_to_ms() { 1215 | let mut color_id_to_tuple = BTreeMap::new(); 1216 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 1217 | 1218 | let texture_size: u32 = 64; 1219 | 1220 | let mut data: Vec = Vec::new(); 1221 | 1222 | for i in 0..texture_size { 1223 | for _ in 0..4 { 1224 | let tile = StoredTilePlacement { 1225 | x: i as u16, 1226 | y: i as u16, 1227 | color_index: 0, 1228 | ms_since_epoch: i as u32, 1229 | }; 1230 | 1231 | tile.write_into(&mut data); 1232 | } 1233 | } 1234 | 1235 | let meta = Meta { 1236 | chunk_descs: vec![], 1237 | color_id_to_tuple, 1238 | last_tile_placed_at_ms_since_epoch: texture_size - 1, 1239 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 1240 | canvas_size_changes: vec![CanvasSizeChange { 1241 | width: texture_size as u16, 1242 | height: texture_size as u16, 1243 | ms_since_epoch: 0, 1244 | }], 1245 | }; 1246 | 1247 | let (device, queue) = TestHelpers::get_device(); 1248 | let mut controller = TextureUpdateByCoords::new( 1249 | &device, 1250 | meta, 1251 | Cursor::new(data), 1252 | Some(wgpu::TextureUsages::COPY_SRC), 1253 | ); 1254 | 1255 | let result = controller.update(&device, &queue, 20, Duration::from_secs(1_000)); 1256 | assert!(matches!( 1257 | result, 1258 | PartialUpdateResult::UpdatedUpToMs { 1259 | max_ms_since_epoch_used: 20, 1260 | did_update_up_to_requested_ms: true 1261 | } 1262 | )); 1263 | 1264 | let buffer = TestHelpers::texture_to_buffer( 1265 | &device, 1266 | &queue, 1267 | &controller.texture, 1268 | controller.texture_extent, 1269 | ); 1270 | TestHelpers::save_debug_image("up_to_ms_0", &buffer); 1271 | for x in 0..texture_size { 1272 | for y in 0..texture_size { 1273 | if x <= 20 && y <= 20 && x == y { 1274 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 1275 | } else { 1276 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 1277 | } 1278 | } 1279 | } 1280 | 1281 | let result = controller.update(&device, &queue, 32, Duration::MAX); 1282 | assert!(matches!( 1283 | result, 1284 | PartialUpdateResult::UpdatedUpToMs { 1285 | max_ms_since_epoch_used: 32, 1286 | did_update_up_to_requested_ms: true 1287 | } 1288 | )); 1289 | 1290 | let buffer = TestHelpers::texture_to_buffer( 1291 | &device, 1292 | &queue, 1293 | &controller.texture, 1294 | controller.texture_extent, 1295 | ); 1296 | TestHelpers::save_debug_image("up_to_ms_1", &buffer); 1297 | for x in 0..texture_size { 1298 | for y in 0..texture_size { 1299 | if x <= 32 && y <= 32 && x == y { 1300 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 1301 | } else { 1302 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 1303 | } 1304 | } 1305 | } 1306 | 1307 | let result = controller.update(&device, &queue, texture_size, Duration::MAX); 1308 | assert!(matches!(result, PartialUpdateResult::ReachedEndOfInput)); 1309 | 1310 | let buffer = TestHelpers::texture_to_buffer( 1311 | &device, 1312 | &queue, 1313 | &controller.texture, 1314 | controller.texture_extent, 1315 | ); 1316 | TestHelpers::save_debug_image("up_to_ms_2", &buffer); 1317 | for x in 0..texture_size { 1318 | for y in 0..texture_size { 1319 | if x == y { 1320 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 1321 | } else { 1322 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 1323 | } 1324 | } 1325 | } 1326 | } 1327 | 1328 | #[test] 1329 | fn up_to_ms_with_holes() { 1330 | let mut color_id_to_tuple = BTreeMap::new(); 1331 | color_id_to_tuple.insert(0, [0, 0, 0, 255]); 1332 | 1333 | let texture_size: u32 = 64; 1334 | 1335 | let mut data: Vec = Vec::new(); 1336 | 1337 | for i in 0..texture_size { 1338 | if i % 2 == 0 { 1339 | continue; 1340 | } 1341 | 1342 | let tile = StoredTilePlacement { 1343 | x: i as u16, 1344 | y: i as u16, 1345 | color_index: 0, 1346 | ms_since_epoch: i as u32, 1347 | }; 1348 | 1349 | tile.write_into(&mut data); 1350 | } 1351 | 1352 | let meta = Meta { 1353 | chunk_descs: vec![], 1354 | color_id_to_tuple, 1355 | last_tile_placed_at_ms_since_epoch: texture_size - 1, 1356 | total_tile_placements: data.len() as u64 / StoredTilePlacement::encoded_size() as u64, 1357 | canvas_size_changes: vec![CanvasSizeChange { 1358 | width: texture_size as u16, 1359 | height: texture_size as u16, 1360 | ms_since_epoch: 0, 1361 | }], 1362 | }; 1363 | 1364 | let (device, queue) = TestHelpers::get_device(); 1365 | let mut controller = TextureUpdateByCoords::new( 1366 | &device, 1367 | meta, 1368 | Cursor::new(data), 1369 | Some(wgpu::TextureUsages::COPY_SRC), 1370 | ); 1371 | 1372 | let result = controller.update(&device, &queue, 20, Duration::MAX); 1373 | // There's no tile placement at 20ms (20 % 2 == 0) so it should have updated up to 19ms 1374 | assert!(matches!( 1375 | result, 1376 | PartialUpdateResult::UpdatedUpToMs { 1377 | max_ms_since_epoch_used: 19, 1378 | did_update_up_to_requested_ms: true 1379 | } 1380 | )); 1381 | 1382 | let buffer = TestHelpers::texture_to_buffer( 1383 | &device, 1384 | &queue, 1385 | &controller.texture, 1386 | controller.texture_extent, 1387 | ); 1388 | TestHelpers::save_debug_image("up_to_ms_with_holes", &buffer); 1389 | for x in 0..texture_size { 1390 | for y in 0..texture_size { 1391 | if x <= 20 && y <= 20 && x == y && x % 2 == 1 { 1392 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 255])); 1393 | } else { 1394 | assert_eq!(buffer.get_pixel(x, y), &Rgba([0, 0, 0, 0])); 1395 | } 1396 | } 1397 | } 1398 | } 1399 | } 1400 | -------------------------------------------------------------------------------- /player/src/transform_generator.rs: -------------------------------------------------------------------------------- 1 | use ultraviolet::{Mat4, Vec2, Vec3}; 2 | 3 | pub struct TransformGenerator { 4 | window_size: (u32, u32), 5 | window_scale_factor: f32, 6 | is_user_panning: bool, 7 | previous_offset: Vec2, 8 | pan_velocity: Vec2, 9 | offset: Vec2, 10 | scale_transform: Mat4, 11 | texture_size: wgpu::Extent3d, 12 | } 13 | 14 | impl TransformGenerator { 15 | pub fn new(window_width: u32, window_height: u32, texture_size: wgpu::Extent3d) -> Self { 16 | Self { 17 | window_size: (window_width, window_height), 18 | window_scale_factor: 1.0, 19 | is_user_panning: false, 20 | previous_offset: Vec2::zero(), 21 | pan_velocity: Vec2::zero(), 22 | offset: Vec2::zero(), 23 | scale_transform: Mat4::identity(), 24 | texture_size, 25 | } 26 | } 27 | 28 | pub fn set_window_scale_factor(&mut self, window_scale_factor: f32) { 29 | self.window_scale_factor = window_scale_factor; 30 | } 31 | 32 | pub fn on_pan_start(&mut self) { 33 | self.is_user_panning = true; 34 | } 35 | 36 | pub fn on_pan_end(&mut self) { 37 | self.is_user_panning = false; 38 | self.pan_velocity *= 0.5; 39 | } 40 | 41 | pub fn apply_translate_diff(&mut self, x: f32, y: f32) { 42 | if x == 0.0 && y == 0.0 { 43 | return; 44 | } 45 | 46 | self.previous_offset = self.offset; 47 | 48 | self.offset.x += (x / self.window_size.0 as f32) * 2.0; 49 | self.offset.y += (y / self.window_size.1 as f32) * 2.0; 50 | 51 | self.pan_velocity = self.offset - self.previous_offset; 52 | } 53 | 54 | pub fn apply_scale_diff(&mut self, diff: f32, origin: Option<(f32, f32)>) { 55 | let origin = Vec2::from(origin.unwrap_or((0.0, 0.0))); 56 | 57 | let scale_around = Vec2::new( 58 | ((origin.x / self.window_size.0 as f32) - 0.5) * 2.0, 59 | -((origin.y / self.window_size.1 as f32) - 0.5) * 2.0, 60 | ) - self.offset; 61 | 62 | let translate = Mat4::from_translation(Vec3::new(-scale_around.x, -scale_around.y, 0.0)); 63 | let translate_back = Mat4::from_translation(Vec3::new(scale_around.x, scale_around.y, 0.0)); 64 | 65 | let scale_amount = if diff > 0.0 { 66 | 1.0 + diff * 0.1 67 | } else { 68 | 1.0 / (1.0 - diff * 0.1) 69 | }; 70 | 71 | let next_scale_transform = 72 | translate_back * Mat4::from_scale(scale_amount) * translate * self.scale_transform; 73 | 74 | let scale_factor = next_scale_transform.cols[0].mag(); 75 | 76 | if scale_factor < 1.0 || scale_factor > 50.0 { 77 | return; 78 | } 79 | 80 | self.scale_transform = next_scale_transform; 81 | } 82 | 83 | pub fn get_transform_matrix(&self) -> Mat4 { 84 | let base_scale = Mat4::from_scale(self.window_scale_factor); 85 | let scale_ratio = Mat4::from_nonuniform_scale(Vec3::new( 86 | self.texture_size.width as f32 / self.window_size.0 as f32, 87 | self.texture_size.height as f32 / self.window_size.1 as f32, 88 | 1.0, 89 | )); 90 | 91 | let base_model_transform = Mat4::from_translation(Vec3::new(-0.5, -0.5, 0.0)); 92 | let translate = Mat4::from_translation(Vec3::new(self.offset.x, self.offset.y, 0.0)); 93 | 94 | let transform = 95 | translate * self.scale_transform * scale_ratio * base_scale * base_model_transform; 96 | 97 | transform 98 | } 99 | 100 | pub fn on_window_resize(&mut self, new_width: u32, new_height: u32) { 101 | self.window_size = (new_width, new_height); 102 | } 103 | 104 | pub fn update(&mut self) { 105 | if !self.is_user_panning && self.pan_velocity != Vec2::zero() { 106 | self.pan_velocity *= 0.93; 107 | self.offset = self.offset + self.pan_velocity; 108 | 109 | if (self.pan_velocity.x.abs() + self.pan_velocity.y.abs()) < f32::EPSILON { 110 | self.pan_velocity = Vec2::zero(); 111 | } 112 | } 113 | } 114 | } 115 | --------------------------------------------------------------------------------