diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-04-20 09:01:27 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-05-14 14:46:13 +0000 |
| commit | 15a5bc845648371619ffbdbc7a740d68c3c26872 (patch) | |
| tree | cad36fafa0d143add0fcf0ff1a2c911e37463ff6 | |
| parent | tsv integration, split binaries (diff) | |
| download | wgsl-view-15a5bc845648371619ffbdbc7a740d68c3c26872.tar.gz wgsl-view-15a5bc845648371619ffbdbc7a740d68c3c26872.zip | |
add tsv-video-buffer
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 875 | ||||
| -rw-r--r-- | Cargo.toml | 13 | ||||
| -rw-r--r-- | build.rs | 53 | ||||
| -rw-r--r-- | ext/hap.c | 1188 | ||||
| -rw-r--r-- | ext/hap.h | 158 | ||||
| -rw-r--r-- | src/bin/tsv_video_buffer.rs | 410 | ||||
| -rw-r--r-- | src/bin/tsv_video_stream.rs | 2 | ||||
| -rw-r--r-- | src/bin/wgsl_render.rs | 60 | ||||
| -rw-r--r-- | src/gpu.rs | 95 | ||||
| -rw-r--r-- | src/hap.rs | 162 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/uniform.rs | 79 | ||||
| -rwxr-xr-x | test.sh | 16 | ||||
| -rwxr-xr-x | test_buffer.sh | 58 | ||||
| -rwxr-xr-x | test_video.sh | 48 |
16 files changed, 2728 insertions, 491 deletions
@@ -1 +1,2 @@ /target +/ext/*.[oa] @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,28 +14,28 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.0", - "getrandom 0.2.15", + "cfg-if 1.0.4", + "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -62,7 +62,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror 2.0.12", + "thiserror 2.0.18", ] [[package]] @@ -138,9 +138,47 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.11.1", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.2", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.1", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.2", + "shlex", + "syn 2.0.117", +] [[package]] name = "bit-set" @@ -189,28 +227,28 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -221,9 +259,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "calloop" @@ -272,16 +310,26 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", ] [[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -289,9 +337,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -305,12 +353,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cpufeatures", "rand_core 0.10.1", ] [[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] name = "clap" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -362,9 +421,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -462,9 +521,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cursor-icon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "cxx" @@ -493,7 +552,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -507,7 +566,7 @@ dependencies = [ "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -525,7 +584,7 @@ dependencies = [ "indexmap 2.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -555,18 +614,18 @@ dependencies = [ [[package]] name = "dlib" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ "libloading", ] [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -584,6 +643,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] name = "env_logger" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -604,12 +669,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -619,6 +684,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] +name = "ffmpeg-next" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c4bd5ab1ac61f29c634df1175d350ded29cf74c3c6d4f7030431a5ae3c7d5d" +dependencies = [ + "bitflags 2.11.1", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a314bc0e022a33a99567ed4bd2576bd58ffd8fcff7891c29194cfecc26a62547" +dependencies = [ + "bindgen 0.72.1", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -648,7 +744,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -683,34 +779,46 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.4", + "windows-link", ] [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", "wasi", ] [[package]] name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", @@ -728,6 +836,12 @@ dependencies = [ ] [[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] name = "glow" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -758,8 +872,8 @@ dependencies = [ "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.12", - "windows 0.58.0", + "thiserror 2.0.18", + "windows 0.62.2", ] [[package]] @@ -770,7 +884,7 @@ checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ "bitflags 2.11.1", "gpu-descriptor-types", - "hashbrown 0.15.2", + "hashbrown 0.15.5", ] [[package]] @@ -788,10 +902,10 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "crunchy", "num-traits", - "zerocopy 0.8.48", + "zerocopy", ] [[package]] @@ -802,9 +916,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash 0.1.5", ] @@ -849,15 +963,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - -[[package]] -name = "hermit-abi" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hexf-parse" @@ -867,9 +975,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "id-arena" @@ -901,13 +1009,22 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.5.0", + "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", ] [[package]] @@ -922,13 +1039,13 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "combine", "jni-macros", "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror 2.0.12", + "thiserror 2.0.18", "walkdir", "windows-link", ] @@ -943,14 +1060,17 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] [[package]] name = "jni-sys" @@ -968,15 +1088,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.4", "libc", ] @@ -986,7 +1107,7 @@ version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "futures-util", "once_cell", "wasm-bindgen", @@ -1023,12 +1144,12 @@ checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ - "cfg-if 1.0.0", - "windows-targets 0.52.6", + "cfg-if 1.0.4", + "windows-link", ] [[package]] @@ -1039,13 +1160,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ "bitflags 2.11.1", "libc", - "redox_syscall 0.5.10", + "plain", + "redox_syscall 0.7.4", ] [[package]] @@ -1071,17 +1193,16 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litrs" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -1093,15 +1214,15 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.4" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -1139,7 +1260,7 @@ dependencies = [ "arrayvec", "bit-set", "bitflags 2.11.1", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cfg_aliases", "codespan-reporting", "half", @@ -1150,9 +1271,9 @@ dependencies = [ "log", "num-traits", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.12", + "thiserror 2.0.18", "unicode-ident", ] @@ -1163,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.11.1", - "jni-sys 0.3.0", + "jni-sys 0.3.1", "log", "ndk-sys", "num_enum", @@ -1183,7 +1304,7 @@ version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys 0.3.0", + "jni-sys 0.3.1", ] [[package]] @@ -1194,7 +1315,7 @@ checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", "memoffset 0.6.5", ] @@ -1206,7 +1327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ "bitflags 2.11.1", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cfg_aliases", "libc", ] @@ -1238,24 +1359,35 @@ dependencies = [ ] [[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + +[[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -1519,24 +1651,25 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "orbclient" -version = "0.3.48" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +checksum = "12c6933ddbbd16539a7672e697bb8d41ac3a4e99ac43eeb40c07236bd7fcb2dd" dependencies = [ + "libc", "libredox", ] [[package]] name = "ordered-float" -version = "4.6.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" dependencies = [ "num-traits", ] @@ -1549,18 +1682,18 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1568,22 +1701,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project" @@ -1602,34 +1735,39 @@ checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "polling" -version = "3.7.4" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi 0.5.2", "pin-project-lite", - "rustix 0.38.44", - "tracing", - "windows-sys 0.59.0", + "rustix 1.1.4", + "windows-sys 0.61.2", ] [[package]] @@ -1646,9 +1784,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -1665,7 +1803,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.48", + "zerocopy", ] [[package]] @@ -1676,67 +1814,73 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.34" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "quick-xml" -version = "0.37.4" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha", @@ -1770,7 +1914,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.17", ] [[package]] @@ -1781,9 +1925,9 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "range-alloc" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" [[package]] name = "raw-window-handle" @@ -1827,18 +1971,27 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ "bitflags 2.11.1", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1848,9 +2001,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1859,9 +2012,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "renderdoc-sys" @@ -1887,6 +2040,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1918,14 +2077,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "same-file" @@ -2000,7 +2159,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -2022,10 +2181,10 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba8593196da75d9dc4f69349682bd4c2099f8cde114257d1ef7ef1b33d1aba54" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "libc", "nix 0.23.2", - "rand 0.8.5", + "rand 0.8.6", "win-sys", ] @@ -2053,27 +2212,24 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slotmap" -version = "1.0.7" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" dependencies = [ "version_check", ] [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay-client-toolkit" @@ -2155,9 +2311,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2174,7 +2330,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2189,7 +2345,6 @@ dependencies = [ [[package]] name = "texture-share-ipc" version = "0.1.0" -source = "git+https://github.com/s-ol/texture-share-vk.git?branch=ash-0.38#c0134ad59b3809d800eea8c0f39f7ac12f4dc120" dependencies = [ "cbindgen", "libc", @@ -2202,7 +2357,6 @@ dependencies = [ [[package]] name = "texture-share-vk-base" version = "0.1.0" -source = "git+https://github.com/s-ol/texture-share-vk.git?branch=ash-0.38#c0134ad59b3809d800eea8c0f39f7ac12f4dc120" dependencies = [ "ash", "cbindgen", @@ -2217,7 +2371,6 @@ dependencies = [ [[package]] name = "texture-share-vk-client" version = "0.1.0" -source = "git+https://github.com/s-ol/texture-share-vk.git?branch=ash-0.38#c0134ad59b3809d800eea8c0f39f7ac12f4dc120" dependencies = [ "cbindgen", "cxx", @@ -2242,11 +2395,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.18", ] [[package]] @@ -2257,18 +2410,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -2311,7 +2464,7 @@ dependencies = [ "arrayref", "arrayvec", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "log", "tiny-skia-path", ] @@ -2338,26 +2491,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap 2.14.0", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ "winnow", ] [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-core", @@ -2365,9 +2531,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" [[package]] name = "ttf-parser" @@ -2377,21 +2543,21 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -2410,6 +2576,12 @@ dependencies = [ ] [[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2427,17 +2599,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -2446,7 +2618,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -2455,7 +2627,7 @@ version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.4", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -2491,7 +2663,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2533,20 +2705,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.1", - "hashbrown 0.15.2", + "hashbrown 0.15.5", "indexmap 2.14.0", "semver", ] [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.1.4", "scoped-tls", "smallvec", "wayland-sys", @@ -2554,12 +2726,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ "bitflags 2.11.1", - "rustix 0.38.44", + "rustix 1.1.4", "wayland-backend", "wayland-scanner", ] @@ -2577,20 +2749,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.4", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ "bitflags 2.11.1", "wayland-backend", @@ -2600,9 +2772,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ "bitflags 2.11.1", "wayland-backend", @@ -2613,9 +2785,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ "bitflags 2.11.1", "wayland-backend", @@ -2626,9 +2798,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", "quick-xml", @@ -2637,9 +2809,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ "dlib", "log", @@ -2676,7 +2848,7 @@ dependencies = [ "arrayvec", "bitflags 2.11.1", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cfg_aliases", "document-features", "hashbrown 0.16.1", @@ -2719,9 +2891,9 @@ dependencies = [ "portable-atomic", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.18", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", @@ -2770,7 +2942,7 @@ dependencies = [ "bitflags 2.11.1", "block2 0.6.2", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.4", "cfg_aliases", "glow", "glutin_wgl_sys", @@ -2800,14 +2972,14 @@ dependencies = [ "raw-window-metal", "renderdoc-sys", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.18", "wasm-bindgen", "wayland-sys", "web-sys", "wgpu-naga-bridge", "wgpu-types", "windows 0.62.2", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -2839,13 +3011,14 @@ name = "wgsl-view" version = "0.1.0" dependencies = [ "ash", + "bindgen 0.71.1", "bytemuck", "env_logger", + "ffmpeg-next", "log", "naga", "pollster", "rosc", - "texture-share-vk-base", "texture-share-vk-client", "wgpu", "wgpu-hal", @@ -2879,11 +3052,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2907,22 +3080,12 @@ dependencies = [ [[package]] name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.62.2", + "windows-core", "windows-future", "windows-numerics", ] @@ -2933,20 +3096,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -2955,11 +3105,11 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-result", + "windows-strings", ] [[package]] @@ -2968,42 +3118,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", "windows-threading", ] [[package]] name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - -[[package]] -name = "windows-implement" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -3014,7 +3142,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -3029,21 +3157,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", ] [[package]] name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" @@ -3053,16 +3172,6 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" @@ -3076,7 +3185,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3085,22 +3194,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link", ] [[package]] @@ -3109,13 +3212,13 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_gnullvm", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_gnullvm", "windows_x86_64_msvc 0.52.6", ] @@ -3130,12 +3233,6 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" @@ -3148,12 +3245,6 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" @@ -3166,12 +3257,6 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" @@ -3190,12 +3275,6 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" @@ -3208,24 +3287,12 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" @@ -3238,12 +3305,6 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" @@ -3302,9 +3363,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -3319,6 +3380,12 @@ dependencies = [ ] [[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] name = "wit-bindgen-core" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3339,7 +3406,7 @@ dependencies = [ "heck 0.5.0", "indexmap 2.14.0", "prettyplease", - "syn 2.0.100", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -3355,7 +3422,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -3410,30 +3477,30 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", "libloading", "once_cell", - "rustix 0.38.44", + "rustix 1.1.4", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xkbcommon-dl" @@ -3456,18 +3523,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.25" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "zerocopy" @@ -3475,18 +3533,7 @@ version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ - "zerocopy-derive 0.8.48", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "zerocopy-derive", ] [[package]] @@ -3497,7 +3544,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.117", ] [[package]] @@ -19,7 +19,15 @@ path = "src/bin/tsv_view.rs" name = "tsv-video-stream" path = "src/bin/tsv_video_stream.rs" +[[bin]] +name = "tsv-video-buffer" +path = "src/bin/tsv_video_buffer.rs" + +[build-dependencies] +bindgen = "0.71" + [dependencies] +ffmpeg-next = "8" winit = "0.30" env_logger = "0.10" log = "0.4" @@ -30,6 +38,5 @@ naga = { version = "29", features = ["wgsl-in"] } pollster = "0.4" rosc = "0.11" bytemuck = { version = "1", features = ["derive"] } -texture-share-vk-client = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "ash-0.38" } -texture-share-vk-base = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "ash-0.38" } - +texture-share-vk-client = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "3d-textures" } +texture-share-vk-base = { git = "https://github.com/s-ol/texture-share-vk.git", branch = "3d-textures" } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..58cbd4b --- /dev/null +++ b/build.rs @@ -0,0 +1,53 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let libdir_path = PathBuf::from("ext") + .canonicalize() + .expect("cannot canonicalize ext/ path"); + + let obj_path = libdir_path.join("hap.o"); + let lib_path = libdir_path.join("libhap.a"); + + println!("cargo:rustc-link-search={}", libdir_path.display()); + println!("cargo:rustc-link-lib=static=hap"); + println!("cargo:rustc-link-lib=snappy"); + println!("cargo:rerun-if-changed=ext/hap.c"); + println!("cargo:rerun-if-changed=ext/hap.h"); + + if !std::process::Command::new("clang") + .arg("-c") + .arg("-o") + .arg(&obj_path) + .arg(libdir_path.join("hap.c")) + .output() + .expect("could not spawn `clang`") + .status + .success() + { + panic!("could not compile hap.c"); + } + + if !std::process::Command::new("ar") + .arg("rcs") + .arg(&lib_path) + .arg(&obj_path) + .output() + .expect("could not spawn `ar`") + .status + .success() + { + panic!("could not create libhap.a"); + } + + let bindings = bindgen::Builder::default() + .header(libdir_path.join("hap.h").to_str().unwrap()) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("unable to generate HAP bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("hap_bindings.rs"); + bindings + .write_to_file(out_path) + .expect("couldn't write HAP bindings"); +} diff --git a/ext/hap.c b/ext/hap.c new file mode 100644 index 0000000..6c492cc --- /dev/null +++ b/ext/hap.c @@ -0,0 +1,1188 @@ +/* + hap.c + + Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hap.h" +#include <stdlib.h> +#include <stdint.h> +#include <string.h> // For memcpy for uncompressed frames +#include "snappy-c.h" + +#define kHapUInt24Max 0x00FFFFFF + +/* + Hap Constants + First four bits represent the compressor + Second four bits represent the format + */ +#define kHapCompressorNone 0xA +#define kHapCompressorSnappy 0xB +#define kHapCompressorComplex 0xC + +#define kHapFormatRGBDXT1 0xB +#define kHapFormatRGBADXT5 0xE +#define kHapFormatYCoCgDXT5 0xF +#define kHapFormatARGTC1 0x1 +#define kHapFormatRGBABPTC 0xC +#define kHapFormatRGBBPTCUF 0x2 +#define kHapFormatRGBBPTCSF 0x3 + +/* + Packed byte values for Hap + + Format Compressor Byte Code + ---------------------------------------------------- + RGB_DXT1 None 0xAB + RGB_DXT1 Snappy 0xBB + RGB_DXT1 Complex 0xCB + RGBA_DXT5 None 0xAE + RGBA_DXT5 Snappy 0xBE + RGBA_DXT5 Complex 0xCE + YCoCg_DXT5 None 0xAF + YCoCg_DXT5 Snappy 0xBF + YCoCg_DXT5 Complex 0xCF + A_RGTC1 None 0xA1 + A_RGTC1 Snappy 0xB1 + A_RGTC1 Complex 0xC1 + RGBA_BPTC_UNORM None 0xAC + RGBA_BPTC_UNORM Snappy 0xBC + RGBA_BPTC_UNORM Complex 0xCC + RGB_BPTC_UNSIGNED_FLOAT None 0xA2 + RGB_BPTC_UNSIGNED_FLOAT Snappy 0xB2 + RGB_BPTC_UNSIGNED_FLOAT Complex 0xC2 + RGB_BPTC_SIGNED_FLOAT None 0xA3 + RGB_BPTC_SIGNED_FLOAT Snappy 0xB3 + RGB_BPTC_SIGNED_FLOAT Complex 0xC3 + */ + +/* + Hap Frame Section Types + */ +#define kHapSectionMultipleImages 0x0D +#define kHapSectionDecodeInstructionsContainer 0x01 +#define kHapSectionChunkSecondStageCompressorTable 0x02 +#define kHapSectionChunkSizeTable 0x03 +#define kHapSectionChunkOffsetTable 0x04 + +/* + To decode we use a struct to store details of each chunk + */ +typedef struct HapChunkDecodeInfo { + unsigned int result; + unsigned int compressor; + const char *compressed_chunk_data; + size_t compressed_chunk_size; + char *uncompressed_chunk_data; + size_t uncompressed_chunk_size; +} HapChunkDecodeInfo; + +// TODO: rename the defines we use for codes used in stored frames +// to better differentiate them from the enums used for the API + +// These read and write little-endian values on big or little-endian architectures +static unsigned int hap_read_3_byte_uint(const void *buffer) +{ + return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16); +} + +static void hap_write_3_byte_uint(void *buffer, unsigned int value) +{ + *(uint8_t *)buffer = value & 0xFF; + *(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF; + *(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF; +} + +static unsigned int hap_read_4_byte_uint(const void *buffer) +{ + return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16) + ((*(((uint8_t *)buffer) + 3)) << 24); +} + +static void hap_write_4_byte_uint(const void *buffer, unsigned int value) +{ + *(uint8_t *)buffer = value & 0xFF; + *(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF; + *(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF; + *(((uint8_t *)buffer) + 3) = (value >> 24) & 0xFF; +} + +#define hap_top_4_bits(x) (((x) & 0xF0) >> 4) + +#define hap_bottom_4_bits(x) ((x) & 0x0F) + +#define hap_4_bit_packed_byte(top_bits, bottom_bits) (((top_bits) << 4) | ((bottom_bits) & 0x0F)) + +static int hap_read_section_header(const void *buffer, uint32_t buffer_length, uint32_t *out_header_length, uint32_t *out_section_length, unsigned int *out_section_type) +{ + /* + Verify buffer is big enough to contain a four-byte header + */ + if (buffer_length < 4U) + { + return HapResult_Bad_Frame; + } + + /* + The first three bytes are the length of the section (not including the header) or zero + if the length is stored in the last four bytes of an eight-byte header + */ + *out_section_length = hap_read_3_byte_uint(buffer); + + /* + If the first three bytes are zero, the size is in the following four bytes + */ + if (*out_section_length == 0U) + { + /* + Verify buffer is big enough to contain an eight-byte header + */ + if (buffer_length < 8U) + { + return HapResult_Bad_Frame; + } + *out_section_length = hap_read_4_byte_uint(((uint8_t *)buffer) + 4U); + *out_header_length = 8U; + } + else + { + *out_header_length = 4U; + } + + /* + The fourth byte stores the section type + */ + *out_section_type = *(((uint8_t *)buffer) + 3U); + + /* + Verify the section does not extend beyond the buffer + */ + if (*out_header_length + *out_section_length > buffer_length) + { + return HapResult_Bad_Frame; + } + + return HapResult_No_Error; +} + +static void hap_write_section_header(void *buffer, size_t header_length, uint32_t section_length, unsigned int section_type) +{ + /* + The first three bytes are the length of the section (not including the header) or zero + if using an eight-byte header + */ + if (header_length == 4U) + { + hap_write_3_byte_uint(buffer, (unsigned int)section_length); + } + else + { + /* + For an eight-byte header, the length is in the last four bytes + */ + hap_write_3_byte_uint(buffer, 0U); + hap_write_4_byte_uint(((uint8_t *)buffer) + 4U, section_length); + } + + /* + The fourth byte stores the section type + */ + *(((uint8_t *)buffer) + 3) = section_type; +} + +// Returns an API texture format constant or 0 if not recognised +static unsigned int hap_texture_format_constant_for_format_identifier(unsigned int identifier) +{ + switch (identifier) + { + case kHapFormatRGBDXT1: + return HapTextureFormat_RGB_DXT1; + case kHapFormatRGBADXT5: + return HapTextureFormat_RGBA_DXT5; + case kHapFormatYCoCgDXT5: + return HapTextureFormat_YCoCg_DXT5; + case kHapFormatARGTC1: + return HapTextureFormat_A_RGTC1; + case kHapFormatRGBABPTC: + return HapTextureFormat_RGBA_BPTC_UNORM; + case kHapFormatRGBBPTCUF: + return HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT; + case kHapFormatRGBBPTCSF: + return HapTextureFormat_RGB_BPTC_SIGNED_FLOAT; + default: + return 0; + + } +} + +// Returns a frame identifier or 0 if not recognised +static unsigned int hap_texture_format_identifier_for_format_constant(unsigned int constant) +{ + switch (constant) + { + case HapTextureFormat_RGB_DXT1: + return kHapFormatRGBDXT1; + case HapTextureFormat_RGBA_DXT5: + return kHapFormatRGBADXT5; + case HapTextureFormat_YCoCg_DXT5: + return kHapFormatYCoCgDXT5; + case HapTextureFormat_A_RGTC1: + return kHapFormatARGTC1; + case HapTextureFormat_RGBA_BPTC_UNORM: + return kHapFormatRGBABPTC; + case HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT: + return kHapFormatRGBBPTCUF; + case HapTextureFormat_RGB_BPTC_SIGNED_FLOAT: + return kHapFormatRGBBPTCSF; + default: + return 0; + } +} + +// Returns the length of a decode instructions container of chunk_count chunks +// not including the section header +static size_t hap_decode_instructions_length(unsigned int chunk_count) +{ + /* + Calculate the size of our Decode Instructions Section + = Second-Stage Compressor Table + Chunk Size Table + headers for both sections + = chunk_count + (4 * chunk_count) + 4 + 4 + */ + size_t length = (5 * chunk_count) + 8; + + return length; +} + +static unsigned int hap_limited_chunk_count_for_frame(size_t input_bytes, unsigned int texture_format, unsigned int chunk_count) +{ + // This is a hard limit due to the 4-byte headers we use for the decode instruction container + // (0xFFFFFF == count + (4 x count) + 20) + if (chunk_count > 3355431) + { + chunk_count = 3355431; + } + // Divide frame equally on DXT block boundries (8 or 16 bytes) + unsigned long dxt_block_count; + switch (texture_format) { + case HapTextureFormat_RGB_DXT1: + case HapTextureFormat_A_RGTC1: + dxt_block_count = input_bytes / 8; + break; + default: + dxt_block_count = input_bytes / 16; + } + while (dxt_block_count % chunk_count != 0) { + chunk_count--; + } + + return chunk_count; +} + +static size_t hap_max_encoded_length(size_t input_bytes, unsigned int texture_format, unsigned int compressor, unsigned int chunk_count) +{ + size_t decode_instructions_length, max_compressed_length; + + chunk_count = hap_limited_chunk_count_for_frame(input_bytes, texture_format, chunk_count); + + decode_instructions_length = hap_decode_instructions_length(chunk_count); + + if (compressor == HapCompressorSnappy) + { + size_t chunk_size = input_bytes / chunk_count; + max_compressed_length = snappy_max_compressed_length(chunk_size) * chunk_count; + } + else + { + max_compressed_length = input_bytes; + } + + // top section header + decode instructions section header + decode instructions + compressed data + return max_compressed_length + 8U + decode_instructions_length + 4U; +} + +unsigned long HapMaxEncodedLength(unsigned int count, + unsigned long *inputBytes, + unsigned int *textureFormats, + unsigned int *chunkCounts) +{ + // Start with the length of a multiple-image section header + unsigned long total_length = 8; + + // Return 0 for bad arguments + if (count == 0 || count > 2 + || inputBytes == NULL + || textureFormats == NULL + || chunkCounts == NULL) + { + return 0; + } + + for (int i = 0; i < count; i++) + { + if (chunkCounts[i] == 0) + { + return 0; + } + + // Assume snappy, the worst case + total_length += hap_max_encoded_length(inputBytes[i], textureFormats[i], HapCompressorSnappy, chunkCounts[i]); + } + + return total_length; +} + +static unsigned int hap_encode_texture(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat, + unsigned int compressor, unsigned int chunkCount, void *outputBuffer, + unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed) +{ + size_t top_section_header_length; + size_t top_section_length; + unsigned int storedCompressor; + unsigned int storedFormat; + + /* + Check arguments + */ + if (inputBuffer == NULL + || inputBufferBytes == 0 + || (textureFormat != HapTextureFormat_RGB_DXT1 + && textureFormat != HapTextureFormat_RGBA_DXT5 + && textureFormat != HapTextureFormat_YCoCg_DXT5 + && textureFormat != HapTextureFormat_A_RGTC1 + && textureFormat != HapTextureFormat_RGBA_BPTC_UNORM + && textureFormat != HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT + && textureFormat != HapTextureFormat_RGB_BPTC_SIGNED_FLOAT + ) + || (compressor != HapCompressorNone + && compressor != HapCompressorSnappy + ) + || outputBuffer == NULL + || outputBufferBytesUsed == NULL + ) + { + return HapResult_Bad_Arguments; + } + else if (outputBufferBytes < hap_max_encoded_length(inputBufferBytes, textureFormat, compressor, chunkCount)) + { + return HapResult_Buffer_Too_Small; + } + + /* + To store frames of length greater than can be expressed in three bytes, we use an eight byte header (the last four bytes are the + frame size). We don't know the compressed size until we have performed compression, but we know the worst-case size + (the uncompressed size), so choose header-length based on that. + + A simpler encoder could always use the eight-byte header variation. + */ + if (inputBufferBytes > kHapUInt24Max) + { + top_section_header_length = 8U; + } + else + { + top_section_header_length = 4U; + } + + if (compressor == HapCompressorSnappy) + { + /* + We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then + store frame uncompressed + */ + + size_t decode_instructions_length; + size_t chunk_size, compress_buffer_remaining; + uint8_t *second_stage_compressor_table; + void *chunk_size_table; + char *compressed_data; + unsigned int i; + + chunkCount = hap_limited_chunk_count_for_frame(inputBufferBytes, textureFormat, chunkCount); + decode_instructions_length = hap_decode_instructions_length(chunkCount); + + // Check we have space for the Decode Instructions Container + if ((inputBufferBytes + decode_instructions_length + 4) > kHapUInt24Max) + { + top_section_header_length = 8U; + } + + second_stage_compressor_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4; + chunk_size_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4 + chunkCount + 4; + + chunk_size = inputBufferBytes / chunkCount; + + // write the Decode Instructions section header + hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length, 4U, decode_instructions_length, kHapSectionDecodeInstructionsContainer); + // write the Second Stage Compressor Table section header + hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U, 4U, chunkCount, kHapSectionChunkSecondStageCompressorTable); + // write the Chunk Size Table section header + hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U + 4U + chunkCount, 4U, chunkCount * 4U, kHapSectionChunkSizeTable); + + compressed_data = (char *)(((uint8_t *)outputBuffer) + top_section_header_length + 4 + decode_instructions_length); + + compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length; + + top_section_length = 4 + decode_instructions_length; + + for (i = 0; i < chunkCount; i++) { + size_t chunk_packed_length = compress_buffer_remaining; + const char *chunk_input_start = (const char *)(((uint8_t *)inputBuffer) + (chunk_size * i)); + if (compressor == HapCompressorSnappy) + { + snappy_status result = snappy_compress(chunk_input_start, chunk_size, (char *)compressed_data, &chunk_packed_length); + if (result != SNAPPY_OK) + { + return HapResult_Internal_Error; + } + } + + if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size) + { + // store the chunk uncompressed + memcpy(compressed_data, chunk_input_start, chunk_size); + chunk_packed_length = chunk_size; + second_stage_compressor_table[i] = kHapCompressorNone; + } + else + { + // ie we used snappy and saved some space + second_stage_compressor_table[i] = kHapCompressorSnappy; + } + hap_write_4_byte_uint(((uint8_t *)chunk_size_table) + (i * 4), chunk_packed_length); + compressed_data += chunk_packed_length; + top_section_length += chunk_packed_length; + compress_buffer_remaining -= chunk_packed_length; + } + + if (top_section_length < inputBufferBytes + top_section_header_length) + { + // use the complex storage because snappy compression saved space + storedCompressor = kHapCompressorComplex; + } + else + { + // Signal to store the frame uncompressed + compressor = HapCompressorNone; + } + } + + if (compressor == HapCompressorNone) + { + memcpy(((uint8_t *)outputBuffer) + top_section_header_length, inputBuffer, inputBufferBytes); + top_section_length = inputBufferBytes; + storedCompressor = kHapCompressorNone; + } + + storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat); + + hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, hap_4_bit_packed_byte(storedCompressor, storedFormat)); + + *outputBufferBytesUsed = top_section_length + top_section_header_length; + + return HapResult_No_Error; +} + +unsigned int HapEncode(unsigned int count, + const void **inputBuffers, unsigned long *inputBuffersBytes, + unsigned int *textureFormats, + unsigned int *compressors, + unsigned int *chunkCounts, + void *outputBuffer, unsigned long outputBufferBytes, + unsigned long *outputBufferBytesUsed) +{ + size_t top_section_header_length; + size_t top_section_length; + unsigned long section_length; + + if (count == 0 || count > 2 // A frame must contain one or two textures + || inputBuffers == NULL + || inputBuffersBytes == NULL + || textureFormats == NULL + || compressors == NULL + || chunkCounts == NULL + || outputBuffer == NULL + || outputBufferBytes == 0 + || outputBufferBytesUsed == NULL) + { + return HapResult_Bad_Arguments; + } + + for (int i = 0; i < count; i++) + { + if (chunkCounts[i] == 0) + { + return HapResult_Bad_Arguments; + } + } + + if (count == 1) + { + // Encode without the multi-image layout + return hap_encode_texture(inputBuffers[0], + inputBuffersBytes[0], + textureFormats[0], + compressors[0], + chunkCounts[0], + outputBuffer, + outputBufferBytes, + outputBufferBytesUsed); + } + else if ((textureFormats[0] != HapTextureFormat_YCoCg_DXT5 && textureFormats[1] != HapTextureFormat_YCoCg_DXT5) + && (textureFormats[0] != HapTextureFormat_A_RGTC1 && textureFormats[1] != HapTextureFormat_A_RGTC1)) + { + /* + Permitted combinations: + HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1 + */ + return HapResult_Bad_Arguments; + } + else + { + // Calculate the worst-case size for the top section and choose a header-length based on that + top_section_length = 0; + for (int i = 0; i < count; i++) + { + top_section_length += inputBuffersBytes[i] + hap_decode_instructions_length(chunkCounts[i]) + 4; + } + + if (top_section_length > kHapUInt24Max) + { + top_section_header_length = 8U; + } + else + { + top_section_header_length = 4U; + } + + // Encode each texture + top_section_length = 0; + for (int i = 0; i < count; i++) + { + void *section = ((uint8_t *)outputBuffer) + top_section_header_length + top_section_length; + unsigned int result = hap_encode_texture(inputBuffers[i], + inputBuffersBytes[i], + textureFormats[i], + compressors[i], + chunkCounts[i], + section, + outputBufferBytes - (top_section_header_length + top_section_length), + §ion_length); + if (result != HapResult_No_Error) + { + return result; + } + top_section_length += section_length; + } + + hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, kHapSectionMultipleImages); + + *outputBufferBytesUsed = top_section_length + top_section_header_length; + + return HapResult_No_Error; + } +} + +static void hap_decode_chunk(HapChunkDecodeInfo chunks[], unsigned int index) +{ + if (chunks) + { + if (chunks[index].compressor == kHapCompressorSnappy) + { + snappy_status snappy_result = snappy_uncompress(chunks[index].compressed_chunk_data, + chunks[index].compressed_chunk_size, + chunks[index].uncompressed_chunk_data, + &chunks[index].uncompressed_chunk_size); + + switch (snappy_result) + { + case SNAPPY_INVALID_INPUT: + chunks[index].result = HapResult_Bad_Frame; + break; + case SNAPPY_OK: + chunks[index].result = HapResult_No_Error; + break; + default: + chunks[index].result = HapResult_Internal_Error; + break; + } + } + else if (chunks[index].compressor == kHapCompressorNone) + { + memcpy(chunks[index].uncompressed_chunk_data, + chunks[index].compressed_chunk_data, + chunks[index].compressed_chunk_size); + chunks[index].result = HapResult_No_Error; + } + else + { + chunks[index].result = HapResult_Bad_Frame; + } + } +} + +static unsigned int hap_decode_header_complex_instructions(const void *texture_section, uint32_t texture_section_length, int * chunk_count, + const void **compressors, const void **chunk_sizes, const void **chunk_offsets, const char **frame_data){ + int result = HapResult_No_Error; + const void *section_start; + uint32_t section_header_length; + uint32_t section_length; + unsigned int section_type; + size_t bytes_remaining = 0; + + *compressors = NULL; + *chunk_sizes = NULL; + *chunk_offsets = NULL; + + result = hap_read_section_header(texture_section, texture_section_length, §ion_header_length, §ion_length, §ion_type); + + if (result == HapResult_No_Error && section_type != kHapSectionDecodeInstructionsContainer) + { + result = HapResult_Bad_Frame; + } + + if (result != HapResult_No_Error) + { + return result; + } + + /* + Frame data follows immediately after the Decode Instructions Container + */ + *frame_data = ((const char *)texture_section) + section_header_length + section_length; + + /* + Step through the sections inside the Decode Instructions Container + */ + section_start = ((uint8_t *)texture_section) + section_header_length; + bytes_remaining = section_length; + + while (bytes_remaining > 0) { + unsigned int section_chunk_count = 0; + result = hap_read_section_header(section_start, bytes_remaining, §ion_header_length, §ion_length, §ion_type); + if (result != HapResult_No_Error) + { + return result; + } + section_start = ((uint8_t *)section_start) + section_header_length; + switch (section_type) { + case kHapSectionChunkSecondStageCompressorTable: + *compressors = section_start; + section_chunk_count = section_length; + break; + case kHapSectionChunkSizeTable: + *chunk_sizes = section_start; + section_chunk_count = section_length / 4; + break; + case kHapSectionChunkOffsetTable: + *chunk_offsets = section_start; + section_chunk_count = section_length / 4; + break; + default: + // Ignore unrecognized sections + break; + } + + /* + If we calculated a chunk count and already have one, make sure they match + */ + if (section_chunk_count != 0) + { + if ((*chunk_count) != 0 && section_chunk_count != (*chunk_count)) + { + return HapResult_Bad_Frame; + } + *chunk_count = section_chunk_count; + } + + section_start = ((uint8_t *)section_start) + section_length; + bytes_remaining -= section_header_length + section_length; + } + + /* + The Chunk Second-Stage Compressor Table and Chunk Size Table are required + */ + if (*compressors == NULL || *chunk_sizes == NULL) + { + return HapResult_Bad_Frame; + } + return result; +} + +unsigned int hap_decode_single_texture(const void *texture_section, uint32_t texture_section_length, + unsigned int texture_section_type, + HapDecodeCallback callback, void *info, + void *outputBuffer, unsigned long outputBufferBytes, + unsigned long *outputBufferBytesUsed, + unsigned int *outputBufferTextureFormat) +{ + int result = HapResult_No_Error; + unsigned int textureFormat; + unsigned int compressor; + size_t bytesUsed = 0; + + /* + One top-level section type describes texture-format and second-stage compression + Hap compressor/format constants can be unpacked by reading the top and bottom four bits. + */ + compressor = hap_top_4_bits(texture_section_type); + textureFormat = hap_bottom_4_bits(texture_section_type); + + /* + Pass the texture format out + */ + *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(textureFormat); + if (*outputBufferTextureFormat == 0) + { + return HapResult_Bad_Frame; + } + + if (compressor == kHapCompressorComplex) + { + /* + The top-level section should contain a Decode Instructions Container followed by frame data + */ + int chunk_count = 0; + const void *compressors = NULL; + const void *chunk_sizes = NULL; + const void *chunk_offsets = NULL; + const char *frame_data = NULL; + + result = hap_decode_header_complex_instructions(texture_section, texture_section_length, &chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data); + + if (result != HapResult_No_Error) + { + return result; + } + + if (chunk_count > 0) + { + /* + Step through the chunks, storing information for their decompression + */ + HapChunkDecodeInfo *chunk_info = (HapChunkDecodeInfo *)malloc(sizeof(HapChunkDecodeInfo) * chunk_count); + + size_t running_compressed_chunk_size = 0; + size_t running_uncompressed_chunk_size = 0; + int i; + + if (chunk_info == NULL) + { + return HapResult_Internal_Error; + } + + for (i = 0; i < chunk_count; i++) { + + chunk_info[i].compressor = *(((uint8_t *)compressors) + i); + + chunk_info[i].compressed_chunk_size = hap_read_4_byte_uint(((uint8_t *)chunk_sizes) + (i * 4)); + + if (chunk_offsets) + { + chunk_info[i].compressed_chunk_data = frame_data + hap_read_4_byte_uint(((uint8_t *)chunk_offsets) + (i * 4)); + } + else + { + chunk_info[i].compressed_chunk_data = frame_data + running_compressed_chunk_size; + } + + running_compressed_chunk_size += chunk_info[i].compressed_chunk_size; + + if (chunk_info[i].compressor == kHapCompressorSnappy) + { + snappy_status snappy_result = snappy_uncompressed_length(chunk_info[i].compressed_chunk_data, + chunk_info[i].compressed_chunk_size, + &(chunk_info[i].uncompressed_chunk_size)); + + if (snappy_result != SNAPPY_OK) + { + switch (snappy_result) + { + case SNAPPY_INVALID_INPUT: + result = HapResult_Bad_Frame; + break; + default: + result = HapResult_Internal_Error; + break; + } + break; + } + } + else + { + chunk_info[i].uncompressed_chunk_size = chunk_info[i].compressed_chunk_size; + } + + chunk_info[i].uncompressed_chunk_data = (char *)(((uint8_t *)outputBuffer) + running_uncompressed_chunk_size); + running_uncompressed_chunk_size += chunk_info[i].uncompressed_chunk_size; + } + + if (result == HapResult_No_Error && running_uncompressed_chunk_size > outputBufferBytes) + { + result = HapResult_Buffer_Too_Small; + } + + if (result == HapResult_No_Error) + { + /* + Perform decompression + */ + bytesUsed = running_uncompressed_chunk_size; + + if (chunk_count == 1) + { + /* + We don't invoke the callback for one chunk, just decode it directly + */ + hap_decode_chunk(chunk_info, 0); + } + else + { + callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info); + } + + /* + Check to see if we encountered any errors and report one of them + */ + for (i = 0; i < chunk_count; i++) + { + if (chunk_info[i].result != HapResult_No_Error) + { + result = chunk_info[i].result; + break; + } + } + } + + free(chunk_info); + + if (result != HapResult_No_Error) + { + return result; + } + } + } + else if (compressor == kHapCompressorSnappy) + { + /* + Only one section is present containing a single block of snappy-compressed texture data + */ + snappy_status snappy_result = snappy_uncompressed_length((const char *)texture_section, texture_section_length, &bytesUsed); + if (snappy_result != SNAPPY_OK) + { + return HapResult_Internal_Error; + } + if (bytesUsed > outputBufferBytes) + { + return HapResult_Buffer_Too_Small; + } + snappy_result = snappy_uncompress((const char *)texture_section, texture_section_length, (char *)outputBuffer, &bytesUsed); + if (snappy_result != SNAPPY_OK) + { + return HapResult_Internal_Error; + } + } + else if (compressor == kHapCompressorNone) + { + /* + Only one section is present containing a single block of uncompressed texture data + */ + bytesUsed = texture_section_length; + if (texture_section_length > outputBufferBytes) + { + return HapResult_Buffer_Too_Small; + } + memcpy(outputBuffer, texture_section, texture_section_length); + } + else + { + return HapResult_Bad_Frame; + } + /* + Fill out the remaining return value + */ + if (outputBufferBytesUsed != NULL) + { + *outputBufferBytesUsed = bytesUsed; + } + + return HapResult_No_Error; +} + +int hap_get_section_at_index(const void *input_buffer, uint32_t input_buffer_bytes, + unsigned int index, + const void **section, uint32_t *section_length, unsigned int *section_type) +{ + int result; + uint32_t section_header_length; + + result = hap_read_section_header(input_buffer, input_buffer_bytes, §ion_header_length, section_length, section_type); + + if (result != HapResult_No_Error) + { + return result; + } + + if (*section_type == kHapSectionMultipleImages) + { + /* + Step through until we find the section at index + */ + size_t offset = 0; + size_t top_section_length = *section_length; + input_buffer = ((uint8_t *)input_buffer) + section_header_length; + section_header_length = 0; + *section_length = 0; + for (int i = 0; i <= index; i++) { + offset += section_header_length + *section_length; + if (offset >= top_section_length) + { + return HapResult_Bad_Arguments; + } + result = hap_read_section_header(((uint8_t *)input_buffer) + offset, + top_section_length - offset, + §ion_header_length, + section_length, + section_type); + if (result != HapResult_No_Error) + { + return result; + } + } + offset += section_header_length; + *section = ((uint8_t *)input_buffer) + offset; + return HapResult_No_Error; + } + else if (index == 0) + { + /* + A single-texture frame with the texture as the top section. + */ + *section = ((uint8_t *)input_buffer) + section_header_length; + return HapResult_No_Error; + } + else + { + *section = NULL; + *section_length = 0; + *section_type = 0; + return HapResult_Bad_Arguments; + } +} + +unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes, + unsigned int index, + HapDecodeCallback callback, void *info, + void *outputBuffer, unsigned long outputBufferBytes, + unsigned long *outputBufferBytesUsed, + unsigned int *outputBufferTextureFormat) +{ + int result = HapResult_No_Error; + const void *section; + uint32_t section_length; + unsigned int section_type; + + /* + Check arguments + */ + if (inputBuffer == NULL + || index > 1 + || callback == NULL + || outputBuffer == NULL + || outputBufferTextureFormat == NULL + ) + { + return HapResult_Bad_Arguments; + } + + /* + Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the + sections inside a multi-image top-level section. + */ + result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); + + if (result == HapResult_No_Error) + { + /* + Decode the located texture + */ + result = hap_decode_single_texture(section, + section_length, + section_type, + callback, info, + outputBuffer, + outputBufferBytes, + outputBufferBytesUsed, + outputBufferTextureFormat); + } + + return result; +} + +unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount) +{ + int result; + uint32_t section_header_length; + uint32_t section_length; + unsigned int section_type; + + result = hap_read_section_header(inputBuffer, inputBufferBytes, §ion_header_length, §ion_length, §ion_type); + + if (result != HapResult_No_Error) + { + return result; + } + + if (section_type == kHapSectionMultipleImages) + { + /* + Step through, counting sections + */ + uint32_t offset = section_header_length; + uint32_t top_section_length = section_length; + *outputTextureCount = 0; + while (offset < top_section_length) { + result = hap_read_section_header(((uint8_t *)inputBuffer) + offset, + inputBufferBytes - offset, + §ion_header_length, + §ion_length, + §ion_type); + if (result != HapResult_No_Error) + { + return result; + } + offset += section_header_length + section_length; + *outputTextureCount += 1; + } + return HapResult_No_Error; + } + else + { + /* + A single-texture frame with the texture as the top section. + */ + *outputTextureCount = 1; + return HapResult_No_Error; + } +} + +unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat) +{ + unsigned int result = HapResult_No_Error; + const void *section; + uint32_t section_length; + unsigned int section_type; + /* + Check arguments + */ + if (inputBuffer == NULL + || index > 1 + || outputBufferTextureFormat == NULL + ) + { + return HapResult_Bad_Arguments; + } + /* + Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the + sections inside a multi-image top-level section. + */ + result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); + + if (result == HapResult_No_Error) + { + /* + Pass the API enum value to match the constant out + */ + *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(hap_bottom_4_bits(section_type)); + /* + Check a valid format was present + */ + if (*outputBufferTextureFormat == 0) + { + result = HapResult_Bad_Frame; + } + } + return result; +} + +unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count) +{ + unsigned int result = HapResult_No_Error; + const void *section; + uint32_t section_length; + unsigned int section_type; + *chunk_count = 0; + + /* + Check arguments + */ + if (inputBuffer == NULL + || index > 1 + ) + { + return HapResult_Bad_Arguments; + } + /* + Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the + sections inside a multi-image top-level section. + */ + result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); + + if (result == HapResult_No_Error) + { + unsigned int compressor; + + /* + One top-level section type describes texture-format and second-stage compression + Hap compressor/format constants can be unpacked by reading the top and bottom four bits. + */ + compressor = hap_top_4_bits(section_type); + + if (compressor == kHapCompressorComplex) + { + /* + The top-level section should contain a Decode Instructions Container followed by frame data + */ + const void *compressors = NULL; + const void *chunk_sizes = NULL; + const void *chunk_offsets = NULL; + const char *frame_data = NULL; + + result = hap_decode_header_complex_instructions(section, section_length, chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data); + + if (result != HapResult_No_Error) + { + return result; + } + } + else if ((compressor == kHapCompressorSnappy)||(compressor == kHapCompressorNone)) + { + *chunk_count = 1; + } + else + { + return HapResult_Bad_Frame; + } + } + return result; +} diff --git a/ext/hap.h b/ext/hap.h new file mode 100644 index 0000000..4f7dd8b --- /dev/null +++ b/ext/hap.h @@ -0,0 +1,158 @@ +/* + hap.h + + Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef hap_h +#define hap_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + These match the constants defined by GL_EXT_texture_compression_s3tc, + GL_ARB_texture_compression_rgtc and GL_ARB_texture_compression_bptc + */ + +enum HapTextureFormat { + HapTextureFormat_RGB_DXT1 = 0x83F0, + HapTextureFormat_RGBA_DXT5 = 0x83F3, + HapTextureFormat_YCoCg_DXT5 = 0x01, + HapTextureFormat_A_RGTC1 = 0x8DBB, + HapTextureFormat_RGBA_BPTC_UNORM = 0x8E8C, + HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, + HapTextureFormat_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, +}; + +enum HapCompressor { + HapCompressorNone, + HapCompressorSnappy +}; + +enum HapResult { + HapResult_No_Error = 0, + HapResult_Bad_Arguments, + HapResult_Buffer_Too_Small, + HapResult_Bad_Frame, + HapResult_Internal_Error +}; + +/* + See HapDecode for descriptions of these function types. + */ +typedef void (*HapDecodeWorkFunction)(void *p, unsigned int index); +typedef void (*HapDecodeCallback)(HapDecodeWorkFunction function, void *p, unsigned int count, void *info); + +/* + Returns the maximum size of an output buffer for a frame composed of one or more textures, or returns 0 on error. + count is the number of textures (1 or 2) and matches the number of values in the array arguments + lengths is an array of input texture lengths in bytes + textureFormats is an array of HapTextureFormats + chunkCounts is an array of chunk counts (1 or more) + */ +unsigned long HapMaxEncodedLength(unsigned int count, + unsigned long *lengths, + unsigned int *textureFormats, + unsigned int *chunkCounts); + +/* + Encodes one or multiple textures into one Hap frame, or returns an error. + + Permitted multiple-texture combinations are: + HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1 + + Use HapMaxEncodedLength() to discover the minimal value for outputBufferBytes. + count is the number of textures (1 or 2) and matches the number of values in the array arguments + inputBuffers is an array of count pointers to texture data + inputBufferBytes is an array of texture data lengths in bytes + textureFormats is an array of HapTextureFormats + compressors is an array of HapCompressors + chunkCounts is an array of chunk counts to permit multithreaded decoding (1 or more) + outputBuffer is the destination buffer to receive the encoded frame + outputBufferBytes is the destination buffer's length in bytes + outputBufferBytesUsed will be set to the actual encoded length of the frame on return +*/ +unsigned int HapEncode(unsigned int count, + const void **inputBuffers, unsigned long *inputBuffersBytes, + unsigned int *textureFormats, + unsigned int *compressors, + unsigned int *chunkCounts, + void *outputBuffer, unsigned long outputBufferBytes, + unsigned long *outputBufferBytesUsed); + +/* + Decodes a texture from inputBuffer which is a Hap frame. + + A frame may contain multiple textures which are to be combined to create the final image. Use HapGetFrameTextureCount() + to discover the number of textures in a frame, and then access each texture by incrementing the index argument to this + function. + + If the frame permits multithreaded decoding, callback will be called once for you to invoke a platform-appropriate + mechanism to assign work to threads, and trigger that work by calling the function passed to your callback the number + of times indicated by the count argument, usually from a number of different threads. This callback must not return + until all the work has been completed. + + void MyHapDecodeCallback(HapDecodeWorkFunction function, void *p, unsigned int count, void *info) + { + int i; + for (i = 0; i < count; i++) { + // Invoke your multithreading mechanism to cause this function to be called + // on a suitable number of threads. + function(p, i); + } + } + info is an argument for your own use to pass context to the callback. + If the frame does not permit multithreaded decoding, callback will not be called. + If outputBufferBytesUsed is not NULL then it will be set to the decoded length of the output buffer. + outputBufferTextureFormat must be non-NULL, and will be set to one of the HapTextureFormat constants. + */ +unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes, + unsigned int index, + HapDecodeCallback callback, void *info, + void *outputBuffer, unsigned long outputBufferBytes, + unsigned long *outputBufferBytesUsed, + unsigned int *outputBufferTextureFormat); + +/* + If this returns HapResult_No_Error then outputTextureCount is set to the count of textures in the frame. + */ +unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount); + +/* + On return sets outputBufferTextureFormat to a HapTextureFormat constant describing the format of the texture at index in the frame. + */ +unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat); + +/* + On return sets chunk_count to the chunk count value of the texture at index in the frame. +*/ +unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bin/tsv_video_buffer.rs b/src/bin/tsv_video_buffer.rs new file mode 100644 index 0000000..7fc94b8 --- /dev/null +++ b/src/bin/tsv_video_buffer.rs @@ -0,0 +1,410 @@ +use std::io::Read; +use std::process::{Command, Stdio}; + +use ash::vk; +use wgsl_view::{gpu, hap}; + +/// Options that ffmpeg supports but ffprobe does not. +const FFMPEG_ONLY_OPTIONS: &[&str] = &["-stream_loop", "-t"]; + +fn filter_probe_args(ff_args: &[String]) -> Vec<String> { + let mut out = Vec::new(); + let mut skip_next = false; + for arg in ff_args { + if skip_next { + skip_next = false; + continue; + } + if FFMPEG_ONLY_OPTIONS.contains(&arg.as_str()) { + skip_next = true; + continue; + } + out.push(arg.clone()); + } + out +} + +struct VideoInfo { + width: u32, + height: u32, + num_frames: u32, + codec_tag: String, +} + +fn probe_video(ff_args: &[String], max_frames: Option<u32>) -> VideoInfo { + let probe_args = filter_probe_args(ff_args); + let output = Command::new("ffprobe") + .args(["-v", "error", "-select_streams", "v:0"]) + .args([ + "-show_entries", + "stream=width,height,nb_frames,codec_tag_string", + ]) + .args(["-of", "csv=p=0"]) + .args(&probe_args) + .output() + .expect("failed to run ffprobe"); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + panic!("ffprobe failed: {stderr}"); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let line = stdout.trim(); + let parts: Vec<&str> = line.split(',').collect(); + if parts.len() < 4 { + panic!("unexpected ffprobe output: {line}"); + } + + let num_frames = parts[3].parse().unwrap_or_else(|_| { + max_frames.unwrap_or_else(|| { + panic!( + "could not determine frame count (nb_frames={}).\n\ + Use --frames N or use tsv-video-stream instead.", + parts[3] + ) + }) + }); + + VideoInfo { + codec_tag: parts[0].to_string(), + width: parts[1].parse().expect("parse width"), + height: parts[2].parse().expect("parse height"), + num_frames, + } +} + +fn is_hap_codec(codec_tag: &str) -> Option<hap::HapFormat> { + match codec_tag { + "Hap1" => Some(hap::HapFormat::Bc1), + "Hap5" => Some(hap::HapFormat::Bc3), + _ => None, + } +} + +fn main() { + env_logger::init(); + + let args: Vec<String> = std::env::args().skip(1).collect(); + let sep = args.iter().position(|a| a == "--"); + + let (own_args, ff_args) = match sep { + Some(i) => (&args[..i], &args[i + 1..]), + None => { + eprintln!("usage: tsv-video-buffer [--name NAME] [--array] [--frames N] -- <ffmpeg input args>"); + std::process::exit(1); + } + }; + + let mut name = "tsv-video-buffer".to_string(); + let mut use_array = false; + let mut max_frames: Option<u32> = None; + + let mut i = 0; + while i < own_args.len() { + match own_args[i].as_str() { + "--name" => { + name = own_args[i + 1].clone(); + i += 2; + } + "--array" => { + use_array = true; + i += 1; + } + "--frames" => { + max_frames = Some(own_args[i + 1].parse().expect("parse --frames")); + i += 2; + } + other => panic!("unknown argument: {other}"), + } + } + + let mut info = probe_video(ff_args, max_frames); + if let Some(max) = max_frames { + info.num_frames = info.num_frames.min(max); + } + let hap_format = is_hap_codec(&info.codec_tag); + + let (texture_format, tsv_img_format) = match hap_format { + Some(hap::HapFormat::Bc1) => ( + wgpu::TextureFormat::Bc1RgbaUnorm, + gpu::ImgFormat::BC1_RGBA, + ), + Some(hap::HapFormat::Bc3) => ( + wgpu::TextureFormat::Bc3RgbaUnorm, + gpu::ImgFormat::BC3_RGBA, + ), + None => (wgpu::TextureFormat::Rgba8UnormSrgb, gpu::ImgFormat::R8G8B8A8), + }; + + log::info!( + "{}x{}, {} frames, codec_tag={}, format={:?}, tsv image: {}", + info.width, + info.height, + info.num_frames, + info.codec_tag, + texture_format, + name + ); + + // Request BC compression features if needed. + let features = if hap_format.is_some() { + wgpu::Features::TEXTURE_COMPRESSION_BC + | if use_array { + wgpu::Features::empty() + } else { + wgpu::Features::TEXTURE_COMPRESSION_BC_SLICED_3D + } + } else { + wgpu::Features::empty() + }; + + let (tsv_img_type, wgpu_dimension) = if use_array { + (gpu::ImgType::D2, wgpu::TextureDimension::D2) + } else { + (gpu::ImgType::D3, wgpu::TextureDimension::D3) + }; + + let instance = gpu::create_instance(); + let adapter = gpu::create_adapter(&instance, None); + let (device, queue) = gpu::create_device_with_features(&adapter, features); + + let mut client = gpu::create_tsv_client(&device); + client + .init_image( + &name, + info.width, + info.height, + info.num_frames, + tsv_img_format, + tsv_img_type, + true, + ) + .expect("init tsv image"); + + let fence = gpu::create_fence(&client); + + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("video_buffer"), + size: wgpu::Extent3d { + width: info.width, + height: info.height, + depth_or_array_layers: info.num_frames, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu_dimension, + format: texture_format, + usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + + if let Some(hap_fmt) = hap_format { + upload_hap_frames(&device, &queue, &texture, ff_args, &info, hap_fmt); + } else { + upload_raw_frames(&device, &queue, &texture, ff_args, &info); + } + + // Submit all pending writes and wait for GPU + queue.submit([]); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + + // Share the complete texture via TSV + let raw = unsafe { gpu::raw_image(&texture) }; + if let Err(e) = client.send_image( + &name, + raw, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + fence, + ) { + log::error!("send_image error: {e}"); + std::process::exit(1); + } + + log::info!("all {} frames uploaded and shared, waiting...", info.num_frames); + + // Keep the process alive so the shared image remains available + loop { + std::thread::sleep(std::time::Duration::from_secs(3600)); + } +} + +fn upload_hap_frames( + _device: &wgpu::Device, + queue: &wgpu::Queue, + texture: &wgpu::Texture, + ff_args: &[String], + info: &VideoInfo, + hap_fmt: hap::HapFormat, +) { + // For HAP, we need raw packet data (not decoded pixels). + // Use ffmpeg-next to read packets directly. + ffmpeg_next::init().expect("ffmpeg init"); + + let (_, input_url) = parse_ff_input_args(ff_args); + let mut ictx = ffmpeg_next::format::input(input_url).expect("open input"); + + let video_stream_index = ictx + .streams() + .best(ffmpeg_next::media::Type::Video) + .expect("no video stream") + .index(); + + let compressed_frame_size = hap_fmt.compressed_size(info.width, info.height); + let mut decode_buf = vec![0u8; compressed_frame_size + 1024 * 1024]; // extra margin + let mut frame_index: u32 = 0; + + let block_bytes = hap_fmt.block_bytes(); + let blocks_x = info.width.div_ceil(4); + + for (stream, packet) in ictx.packets() { + if stream.index() != video_stream_index { + continue; + } + + let data = packet.data().expect("packet data"); + let (_, decoded_len) = hap::decode(data, &mut decode_buf).expect("HAP decode"); + + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: frame_index, + }, + aspect: wgpu::TextureAspect::All, + }, + &decode_buf[..decoded_len], + wgpu::TexelCopyBufferLayout { + offset: 0, + // BC: bytes_per_row = blocks_x * block_bytes + bytes_per_row: Some(blocks_x * block_bytes), + // rows_per_image = height in blocks + rows_per_image: Some(info.height.div_ceil(4)), + }, + wgpu::Extent3d { + width: info.width, + height: info.height, + depth_or_array_layers: 1, + }, + ); + + frame_index += 1; + if frame_index.is_multiple_of(10) || frame_index == info.num_frames { + log::info!("uploaded frame {}/{}", frame_index, info.num_frames); + } + if frame_index >= info.num_frames { + break; + } + } + + if frame_index < info.num_frames { + log::warn!( + "expected {} frames but only decoded {frame_index}", + info.num_frames + ); + } +} + +fn upload_raw_frames( + _device: &wgpu::Device, + queue: &wgpu::Queue, + texture: &wgpu::Texture, + ff_args: &[String], + info: &VideoInfo, +) { + let frame_size = (info.width * info.height * 4) as usize; + + let mut ffmpeg = Command::new("ffmpeg") + .args(["-v", "quiet"]) + .args(ff_args) + .args(["-f", "rawvideo", "-pix_fmt", "rgba", "pipe:1"]) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .expect("failed to start ffmpeg"); + + let stdout = ffmpeg.stdout.take().unwrap(); + let mut reader = std::io::BufReader::new(stdout); + let mut frame_buf = vec![0u8; frame_size]; + let mut frame_index: u32 = 0; + + while reader.read_exact(&mut frame_buf).is_ok() { + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: frame_index, + }, + aspect: wgpu::TextureAspect::All, + }, + &frame_buf, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(info.width * 4), + rows_per_image: Some(info.height), + }, + wgpu::Extent3d { + width: info.width, + height: info.height, + depth_or_array_layers: 1, + }, + ); + + frame_index += 1; + if frame_index.is_multiple_of(10) || frame_index == info.num_frames { + log::info!("uploaded frame {}/{}", frame_index, info.num_frames); + } + if frame_index >= info.num_frames { + break; + } + } + + // Drop the reader/stdout to close the pipe — this signals ffmpeg to exit + // (important when --frames truncates before ffmpeg finishes) + drop(reader); + ffmpeg.wait().ok(); + + if frame_index < info.num_frames { + log::warn!( + "expected {} frames but only decoded {frame_index}", + info.num_frames + ); + } +} + +/// Parse ffmpeg-style input args to extract -f <format> and the input URL. +/// Returns (Option<format>, input_url). +fn parse_ff_input_args(ff_args: &[String]) -> (Option<String>, &str) { + let mut format = None; + let mut input_url = None; + let mut i = 0; + + while i < ff_args.len() { + match ff_args[i].as_str() { + "-f" => { + format = Some(ff_args[i + 1].clone()); + i += 2; + } + "-i" => { + input_url = Some(ff_args[i + 1].as_str()); + i += 2; + } + _ => { + // Last positional arg is input URL if no -i was given + if i == ff_args.len() - 1 && input_url.is_none() { + input_url = Some(ff_args[i].as_str()); + } + i += 1; + } + } + } + + (format, input_url.expect("no input file specified in ffmpeg args")) +} diff --git a/src/bin/tsv_video_stream.rs b/src/bin/tsv_video_stream.rs index d68f4b4..22b38f8 100644 --- a/src/bin/tsv_video_stream.rs +++ b/src/bin/tsv_video_stream.rs @@ -98,7 +98,7 @@ fn main() { let mut client = gpu::create_tsv_client(&device); client - .init_image(&name, width, height, gpu::TSV_FORMAT, true) + .init_image(&name, width, height, 1, gpu::TSV_FORMAT, gpu::ImgType::D2, true) .expect("init tsv image"); let fence = gpu::create_fence(&client); diff --git a/src/bin/wgsl_render.rs b/src/bin/wgsl_render.rs index 1860b7c..29b5d5d 100644 --- a/src/bin/wgsl_render.rs +++ b/src/bin/wgsl_render.rs @@ -33,11 +33,15 @@ fn main() { let instance = gpu::create_instance(); let adapter = gpu::create_adapter(&instance, None); - let (device, queue) = gpu::create_device(&adapter); + let (device, queue) = gpu::create_device_with_features( + &adapter, + wgpu::Features::TEXTURE_COMPRESSION_BC + | wgpu::Features::TEXTURE_COMPRESSION_BC_SLICED_3D, + ); let mut client = gpu::create_tsv_client(&device); client - .init_image(&name, width, height, gpu::TSV_FORMAT, true) + .init_image(&name, width, height, 1, gpu::TSV_FORMAT, gpu::ImgType::D2, true) .expect("init tsv image"); let fence = gpu::create_fence(&client); @@ -114,7 +118,7 @@ fn main() { }; if dirty { - refresh_textures(renderer.uniforms(), &device, &mut client, fence); + gpu::refresh_textures(renderer.uniforms(), &device, &mut client, fence); renderer.render(&device, &queue); device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); @@ -228,54 +232,4 @@ fn configure_sampler( Ok(()) } -/// Refresh all texture inputs from TSV shared images. -fn refresh_textures( - cache: &mut wgsl_view::uniform::UniformCache, - device: &wgpu::Device, - client: &mut texture_share_vk_client::VkClient, - fence: vk::Fence, -) { - let mut needs_rebind = false; - - for slot in cache.texture_slots_mut() { - let tsv_name = match slot.tsv_name() { - Some(n) => n.to_string(), - None => continue, - }; - - if !slot.tsv_registered() { - if let Err(e) = client.find_image(&tsv_name, true) { - log::debug!("tsv find '{tsv_name}': {e}"); - continue; - } - - match client.find_image_data(&tsv_name, true) { - Ok(Some((_lock, data))) => { - if slot.resize(device, data.width, data.height) { - needs_rebind = true; - } - } - _ => continue, - } - - slot.set_tsv_registered(); - } - let raw = unsafe { gpu::raw_image(slot.texture()) }; - match client.recv_image( - &tsv_name, - raw, - vk::ImageLayout::UNDEFINED, - vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, - fence, - ) { - Ok(Some(())) => log::trace!("texture '{}' updated from '{tsv_name}'", slot.name()), - Ok(None) => {} - Err(e) => log::warn!("recv_image '{tsv_name}': {e}"), - } - } - - if needs_rebind { - cache.rebuild_bind_group(device); - } -} @@ -38,8 +38,17 @@ pub fn create_adapter( /// Requests a wgpu device + queue. pub fn create_device(adapter: &wgpu::Adapter) -> (wgpu::Device, wgpu::Queue) { + create_device_with_features(adapter, wgpu::Features::empty()) +} + +/// Requests a wgpu device + queue with additional features. +pub fn create_device_with_features( + adapter: &wgpu::Adapter, + features: wgpu::Features, +) -> (wgpu::Device, wgpu::Queue) { pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { label: Some("device"), + required_features: features, ..Default::default() })) .expect("create device") @@ -116,6 +125,32 @@ pub fn create_tsv_client(device: &wgpu::Device) -> VkClient { pub const TSV_FORMAT: texture_share_vk_base::ipc::platform::img_data::ImgFormat = texture_share_vk_base::ipc::platform::img_data::ImgFormat::R8G8B8A8; +/// Re-export ImgFormat and ImgType for use in binaries. +pub use texture_share_vk_base::ipc::platform::img_data::{ImgFormat, ImgType}; + +/// Convert a TSV ImgFormat to a wgpu TextureFormat. +pub fn img_format_to_wgpu(fmt: ImgFormat) -> wgpu::TextureFormat { + match fmt { + ImgFormat::R8G8B8A8 => wgpu::TextureFormat::Rgba8UnormSrgb, + ImgFormat::R8G8B8 => wgpu::TextureFormat::Rgba8UnormSrgb, + ImgFormat::B8G8R8A8 => wgpu::TextureFormat::Bgra8UnormSrgb, + ImgFormat::B8G8R8 => wgpu::TextureFormat::Bgra8UnormSrgb, + ImgFormat::BC1_RGBA => wgpu::TextureFormat::Bc1RgbaUnorm, + ImgFormat::BC3_RGBA => wgpu::TextureFormat::Bc3RgbaUnorm, + ImgFormat::BC7_RGBA => wgpu::TextureFormat::Bc7RgbaUnorm, + ImgFormat::Undefined => wgpu::TextureFormat::Rgba8UnormSrgb, + } +} + +/// Returns the wgpu Features required for a TSV image format. +pub fn img_format_features(fmt: ImgFormat) -> wgpu::Features { + if fmt.is_compressed() { + wgpu::Features::TEXTURE_COMPRESSION_BC + } else { + wgpu::Features::empty() + } +} + /// Extracts the raw vk::Image handle from a wgpu Texture. /// /// # Safety @@ -139,3 +174,63 @@ pub fn destroy_fence(client: &VkClient, fence: vk::Fence) { let device = &client.get_vk_setup().device.device; unsafe { device.destroy_fence(fence, None) }; } + +/// Refresh all texture inputs from TSV shared images. +/// Handles format detection and texture recreation internally. +pub fn refresh_textures( + cache: &mut crate::uniform::UniformCache, + device: &wgpu::Device, + client: &mut VkClient, + fence: vk::Fence, +) { + let mut needs_rebind = false; + + for slot in cache.texture_slots_mut() { + let tsv_name = match slot.tsv_name() { + Some(n) => n.to_string(), + None => continue, + }; + + if !slot.tsv_registered() { + if let Err(e) = client.find_image(&tsv_name, true) { + log::debug!("tsv find '{tsv_name}': {e}"); + continue; + } + + match client.find_image_data(&tsv_name, true) { + Ok(Some((_lock, data))) => { + let format = img_format_to_wgpu(data.format); + if slot.resize( + device, + data.width, + data.height, + data.depth_or_array_layers, + format, + ) { + needs_rebind = true; + } + } + _ => continue, + } + + slot.set_tsv_registered(); + } + + let raw = unsafe { raw_image(slot.texture()) }; + match client.recv_image( + &tsv_name, + raw, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + fence, + ) { + Ok(Some(())) => log::trace!("texture '{}' updated from '{tsv_name}'", slot.name()), + Ok(None) => {} + Err(e) => log::warn!("recv_image '{tsv_name}': {e}"), + } + } + + if needs_rebind { + cache.rebuild_bind_group(device); + } +} diff --git a/src/hap.rs b/src/hap.rs new file mode 100644 index 0000000..2189d7b --- /dev/null +++ b/src/hap.rs @@ -0,0 +1,162 @@ +//! Safe Rust wrapper around the HAP codec C library. +//! +//! HAP is a video codec that stores GPU-compressed DXT/BC texture data +//! (optionally Snappy-compressed). This module decodes HAP frames into +//! raw BC block data suitable for direct GPU upload. + +#[allow(non_upper_case_globals)] +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod ffi { + include!(concat!(env!("OUT_DIR"), "/hap_bindings.rs")); +} + +use std::fmt; + +/// Compressed texture format produced by HAP decoding. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HapFormat { + /// DXT1 / BC1 — 4 bits per pixel, RGB with 1-bit alpha + Bc1, + /// DXT5 / BC3 — 8 bits per pixel, RGB with full alpha + Bc3, +} + +impl HapFormat { + /// Returns the corresponding wgpu texture format. + pub fn wgpu_format(self) -> wgpu::TextureFormat { + match self { + HapFormat::Bc1 => wgpu::TextureFormat::Bc1RgbaUnorm, + HapFormat::Bc3 => wgpu::TextureFormat::Bc3RgbaUnorm, + } + } + + /// Bytes per 4x4 block for this format. + pub fn block_bytes(self) -> u32 { + match self { + HapFormat::Bc1 => 8, + HapFormat::Bc3 => 16, + } + } + + /// Compute the byte size of a compressed image with the given pixel dimensions. + pub fn compressed_size(self, width: u32, height: u32) -> usize { + let blocks_x = width.div_ceil(4); + let blocks_y = height.div_ceil(4); + (blocks_x * blocks_y * self.block_bytes()) as usize + } +} + +impl fmt::Display for HapFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HapFormat::Bc1 => write!(f, "BC1/DXT1"), + HapFormat::Bc3 => write!(f, "BC3/DXT5"), + } + } +} + +/// Error type for HAP decoding operations. +#[derive(Debug)] +pub enum HapError { + BadArguments, + BufferTooSmall, + BadFrame, + InternalError, + UnsupportedFormat(u32), +} + +impl fmt::Display for HapError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + HapError::BadArguments => write!(f, "HAP: bad arguments"), + HapError::BufferTooSmall => write!(f, "HAP: buffer too small"), + HapError::BadFrame => write!(f, "HAP: bad frame data"), + HapError::InternalError => write!(f, "HAP: internal error"), + HapError::UnsupportedFormat(fmt) => write!(f, "HAP: unsupported texture format 0x{fmt:04x}"), + } + } +} + +impl std::error::Error for HapError {} + +fn result_from_code(code: u32) -> Result<(), HapError> { + match code { + 0 => Ok(()), + 1 => Err(HapError::BadArguments), + 2 => Err(HapError::BufferTooSmall), + 3 => Err(HapError::BadFrame), + _ => Err(HapError::InternalError), + } +} + +fn format_from_raw(raw: u32) -> Result<HapFormat, HapError> { + match raw { + ffi::HapTextureFormat_HapTextureFormat_RGB_DXT1 => Ok(HapFormat::Bc1), + ffi::HapTextureFormat_HapTextureFormat_RGBA_DXT5 => Ok(HapFormat::Bc3), + other => Err(HapError::UnsupportedFormat(other)), + } +} + +/// Callback passed to HapDecode for multi-threaded chunk decoding. +/// We decode sequentially (sufficient for our use case). +unsafe extern "C" fn decode_callback( + function: ffi::HapDecodeWorkFunction, + p: *mut std::os::raw::c_void, + count: std::os::raw::c_uint, + _info: *mut std::os::raw::c_void, +) { + if let Some(func) = function { + for i in 0..count { + func(p, i); + } + } +} + +/// Decode a HAP video packet into compressed BC texture data. +/// +/// `packet_data` is the raw packet data from ffmpeg. +/// `output_buf` is a reusable buffer that will be resized as needed. +/// +/// Returns the format and the number of valid bytes written to `output_buf`. +pub fn decode( + packet_data: &[u8], + output_buf: &mut Vec<u8>, +) -> Result<(HapFormat, usize), HapError> { + let mut output_length: u64 = 0; + let mut raw_format: u32 = 0; + + let result = unsafe { + ffi::HapDecode( + packet_data.as_ptr() as *const std::os::raw::c_void, + packet_data.len() as u64, + 0, // texture index (always 0 for single-texture HAP) + Some(decode_callback), + std::ptr::null_mut(), + output_buf.as_mut_ptr() as *mut std::os::raw::c_void, + output_buf.len() as u64, + &mut output_length, + &mut raw_format, + ) + }; + + result_from_code(result)?; + let format = format_from_raw(raw_format)?; + Ok((format, output_length as usize)) +} + +/// Detect the texture format of a HAP frame without fully decoding it. +pub fn get_frame_format(packet_data: &[u8]) -> Result<HapFormat, HapError> { + let mut raw_format: u32 = 0; + let result = unsafe { + ffi::HapGetFrameTextureFormat( + packet_data.as_ptr() as *const std::os::raw::c_void, + packet_data.len() as u64, + 0, + &mut raw_format, + ) + }; + result_from_code(result)?; + format_from_raw(raw_format) +} @@ -1,4 +1,5 @@ pub mod gpu; +pub mod hap; pub mod osc; pub mod renderer; pub mod uniform; diff --git a/src/uniform.rs b/src/uniform.rs index 57a0a4b..44fb541 100644 --- a/src/uniform.rs +++ b/src/uniform.rs @@ -405,8 +405,10 @@ pub struct TextureSlot { tsv_registered: bool, texture: wgpu::Texture, view: wgpu::TextureView, + format: wgpu::TextureFormat, width: u32, height: u32, + depth_or_array_layers: u32, view_dimension: wgpu::TextureViewDimension, sample_type: wgpu::TextureSampleType, multisampled: bool, @@ -421,7 +423,10 @@ impl TextureSlot { sample_type: wgpu::TextureSampleType, multisampled: bool, ) -> Self { - let (texture, view) = create_input_texture(device, &name, 1, 1); + let format = wgpu::TextureFormat::Rgba8UnormSrgb; + let dimension = view_dimension_to_texture_dimension(view_dimension); + let (texture, view) = + create_input_texture(device, &name, 1, 1, 1, dimension, view_dimension, format); Self { name, binding, @@ -429,8 +434,10 @@ impl TextureSlot { tsv_registered: false, texture, view, + format, width: 1, height: 1, + depth_or_array_layers: 1, view_dimension, sample_type, multisampled, @@ -472,41 +479,82 @@ impl TextureSlot { self.height } - /// Recreate the texture at new dimensions. Returns true if the size actually changed. - pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) -> bool { - if self.width == width && self.height == height { + /// Recreate the texture if size or format changed. Returns true if recreated. + pub fn resize( + &mut self, + device: &wgpu::Device, + width: u32, + height: u32, + depth_or_array_layers: u32, + format: wgpu::TextureFormat, + ) -> bool { + if self.width == width + && self.height == height + && self.depth_or_array_layers == depth_or_array_layers + && self.format == format + { return false; } - let (texture, view) = create_input_texture(device, &self.name, width, height); + let dimension = view_dimension_to_texture_dimension(self.view_dimension); + let (texture, view) = create_input_texture( + device, + &self.name, + width, + height, + depth_or_array_layers, + dimension, + self.view_dimension, + format, + ); self.texture = texture; self.view = view; + self.format = format; self.width = width; self.height = height; + self.depth_or_array_layers = depth_or_array_layers; true } } +fn view_dimension_to_texture_dimension( + view_dim: wgpu::TextureViewDimension, +) -> wgpu::TextureDimension { + match view_dim { + wgpu::TextureViewDimension::D1 => wgpu::TextureDimension::D1, + wgpu::TextureViewDimension::D3 => wgpu::TextureDimension::D3, + // D2, D2Array, Cube, CubeArray all use D2 textures + _ => wgpu::TextureDimension::D2, + } +} + fn create_input_texture( device: &wgpu::Device, label: &str, width: u32, height: u32, + depth_or_array_layers: u32, + dimension: wgpu::TextureDimension, + view_dimension: wgpu::TextureViewDimension, + format: wgpu::TextureFormat, ) -> (wgpu::Texture, wgpu::TextureView) { let texture = device.create_texture(&wgpu::TextureDescriptor { label: Some(label), size: wgpu::Extent3d { width, height, - depth_or_array_layers: 1, + depth_or_array_layers, }, mip_level_count: 1, sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, + dimension, + format, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, view_formats: &[], }); - let view = texture.create_view(&Default::default()); + let view = texture.create_view(&wgpu::TextureViewDescriptor { + dimension: Some(view_dimension), + ..Default::default() + }); (texture, view) } @@ -702,8 +750,17 @@ impl UniformCache { { slot.tsv_name = old.tsv_name.clone(); slot.tsv_registered = old.tsv_registered; - if old.width > 1 || old.height > 1 { - slot.resize(device, old.width, old.height); + if old.width > 1 + || old.height > 1 + || old.depth_or_array_layers > 1 + { + slot.resize( + device, + old.width, + old.height, + old.depth_or_array_layers, + old.format, + ); } } } @@ -5,6 +5,9 @@ HOST=localhost PORT=9000 TSV_SRC=test-vid +# ensure relatively predictable timing for spawn() +cargo build + PIDS=() cleanup() { for pid in "${PIDS[@]}"; do @@ -21,25 +24,20 @@ spawn() { PIDS+=($!) } -spawn tsv-video-stream --name "$TSV_SRC" -- \ - -f lavfi -i "testsrc=size=1920x1080:rate=60" - -spawn wgsl-render --continuous +spawn wgsl-render sleep 2 SHADER=' -@group(1) @binding(0) var video: texture_2d<f32>; -@group(1) @binding(1) var s: sampler; +@group(1) @binding(0) var<uniform> color: vec3f; @fragment fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { - return textureSample(video, s, uv).rrra; + return vec4f(color, 1); } ' oscsend "$HOST" "$PORT" /shader s "$SHADER" -oscsend "$HOST" "$PORT" /texture/video s "$TSV_SRC" -oscsend "$HOST" "$PORT" /sampler/sampler ss nearest clamp +oscsend "$HOST" "$PORT" /uniform/color fff 1 0 0 cargo run --bin tsv-view diff --git a/test_buffer.sh b/test_buffer.sh new file mode 100755 index 0000000..745227e --- /dev/null +++ b/test_buffer.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -euo pipefail + +HOST=localhost +PORT=9000 +TSV_SRC=test-vid + +# ensure relatively predictable timing for spawn() +cargo build + +PIDS=() +cleanup() { + for pid in "${PIDS[@]}"; do + kill "$pid" 2>/dev/null || true + done + wait 2>/dev/null +} +trap cleanup EXIT + +spawn() { + BIN="$1" + shift + cargo run --bin "$BIN" -- "$@" & + PIDS+=($!) +} + +spawn tsv-video-buffer --name "$TSV_SRC" --frames 60 -- \ + -f lavfi -i "testsrc=size=1920x1080:rate=60" +spawn wgsl-render + +sleep 2 + +SHADER=' +@group(1) @binding(0) var<uniform> time: f32; + +@group(1) @binding(1) var video: texture_3d<f32>; +@group(1) @binding(2) var s: sampler; + +@fragment +fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { + let dims = textureDimensions(video); + let frame = fract(time / f32(dims.z)); + return textureSampleLevel(video, s, vec3f(uv, frame), 0.0); +} +' +oscsend "$HOST" "$PORT" /shader s "$SHADER" +oscsend "$HOST" "$PORT" /texture/video s "$TSV_SRC" +oscsend "$HOST" "$PORT" /sampler/s ss nearest repeat + +spawn tsv-view + +echo "Playing video buffer (Ctrl+C to stop)..." +T=0 +while true; do + oscsend "$HOST" "$PORT" /uniform/time f "$T" + T=$((T+1 % 60)) + sleep 0.016 +done diff --git a/test_video.sh b/test_video.sh new file mode 100755 index 0000000..a91f2cb --- /dev/null +++ b/test_video.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail + +HOST=localhost +PORT=9000 +TSV_SRC=test-vid + +# ensure relatively predictable timing for spawn() +cargo build + +PIDS=() +cleanup() { + for pid in "${PIDS[@]}"; do + kill "$pid" 2>/dev/null || true + done + wait 2>/dev/null +} +trap cleanup EXIT + +spawn() { + BIN="$1" + shift + cargo run --bin "$BIN" -- "$@" & + PIDS+=($!) +} + +spawn tsv-video-stream --name "$TSV_SRC" -- \ + -f lavfi -i "testsrc=size=1920x1080:rate=60" + +spawn wgsl-render --continuous + +sleep 2 + + +SHADER=' +@group(1) @binding(0) var video: texture_2d<f32>; +@group(1) @binding(1) var s: sampler; + +@fragment +fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { + return textureSample(video, s, uv).rrra; +} +' +oscsend "$HOST" "$PORT" /shader s "$SHADER" +oscsend "$HOST" "$PORT" /texture/video s "$TSV_SRC" +oscsend "$HOST" "$PORT" /sampler/sampler ss nearest clamp + +cargo run --bin tsv-view |
