summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-04-20 09:01:27 +0000
committers-ol <s+removethis@s-ol.nu>2026-05-14 14:46:13 +0000
commit15a5bc845648371619ffbdbc7a740d68c3c26872 (patch)
treecad36fafa0d143add0fcf0ff1a2c911e37463ff6
parenttsv integration, split binaries (diff)
downloadwgsl-view-15a5bc845648371619ffbdbc7a740d68c3c26872.tar.gz
wgsl-view-15a5bc845648371619ffbdbc7a740d68c3c26872.zip
add tsv-video-buffer
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock875
-rw-r--r--Cargo.toml13
-rw-r--r--build.rs53
-rw-r--r--ext/hap.c1188
-rw-r--r--ext/hap.h158
-rw-r--r--src/bin/tsv_video_buffer.rs410
-rw-r--r--src/bin/tsv_video_stream.rs2
-rw-r--r--src/bin/wgsl_render.rs60
-rw-r--r--src/gpu.rs95
-rw-r--r--src/hap.rs162
-rw-r--r--src/lib.rs1
-rw-r--r--src/uniform.rs79
-rwxr-xr-xtest.sh16
-rwxr-xr-xtest_buffer.sh58
-rwxr-xr-xtest_video.sh48
16 files changed, 2728 insertions, 491 deletions
diff --git a/.gitignore b/.gitignore
index ea8c4bf..b7aa35a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/target
+/ext/*.[oa]
diff --git a/Cargo.lock b/Cargo.lock
index 4762e5b..a426584 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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]]
diff --git a/Cargo.toml b/Cargo.toml
index e919911..656e123 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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),
+ &section_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, &section_header_length, &section_length, &section_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, &section_header_length, &section_length, &section_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, &section_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,
+ &section_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, &section, &section_length, &section_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, &section_header_length, &section_length, &section_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,
+ &section_header_length,
+ &section_length,
+ &section_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, &section, &section_length, &section_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, &section, &section_length, &section_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);
- }
-}
diff --git a/src/gpu.rs b/src/gpu.rs
index e6529f9..993e524 100644
--- a/src/gpu.rs
+++ b/src/gpu.rs
@@ -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)
+}
diff --git a/src/lib.rs b/src/lib.rs
index 22a55d8..57a1042 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
+ );
}
}
}
diff --git a/test.sh b/test.sh
index ccc5e8f..6ed469e 100755
--- a/test.sh
+++ b/test.sh
@@ -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