feat: ui sprites + openxr skybox
This commit is contained in:
385
Cargo.lock
generated
385
Cargo.lock
generated
@@ -86,7 +86,7 @@ dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cc",
|
||||
"cesu8",
|
||||
"jni 0.21.1",
|
||||
"jni",
|
||||
"jni-sys",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -207,9 +207,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
||||
checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@@ -229,7 +229,7 @@ version = "0.38.0+1.3.281"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
|
||||
dependencies = [
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -276,9 +276,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0"
|
||||
checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
@@ -367,14 +367,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-signal"
|
||||
version = "0.2.8"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d"
|
||||
checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"async-lock",
|
||||
@@ -402,7 +402,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -456,7 +456,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -468,7 +468,7 @@ dependencies = [
|
||||
"autocxx-engine",
|
||||
"env_logger",
|
||||
"indexmap 1.9.3",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -494,7 +494,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustversion",
|
||||
"serde_json",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"version_check",
|
||||
@@ -510,7 +510,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -527,7 +527,7 @@ dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -552,6 +552,12 @@ version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bcdec_rs"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9934c2b68e46448d814db20e34a840ef9b4e7b3b7c8b1da91161481230f6350"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.69.4"
|
||||
@@ -570,7 +576,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -642,7 +648,7 @@ checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -653,23 +659,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"log",
|
||||
"polling",
|
||||
"rustix",
|
||||
"slab",
|
||||
"thiserror",
|
||||
]
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
@@ -685,25 +677,13 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop-wayland-source"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
|
||||
dependencies = [
|
||||
"calloop 0.12.4",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop-wayland-source"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
|
||||
dependencies = [
|
||||
"calloop 0.13.0",
|
||||
"calloop",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
@@ -711,13 +691,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.0"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8"
|
||||
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -807,14 +786,14 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.9"
|
||||
version = "4.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||
checksum = "8f6b81fb3c84f5563d509c59b5a48d935f689e993afa90fe39047f05adef9142"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -822,9 +801,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.9"
|
||||
version = "4.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||
checksum = "5ca6706fd5224857d9ac5eb9355f6683563cc0541c7cd9d014043b57cbec78ac"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -841,7 +820,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1034,7 +1013,7 @@ dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-rs",
|
||||
"dasp_sample",
|
||||
"jni 0.21.1",
|
||||
"jni",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"mach2",
|
||||
@@ -1153,7 +1132,7 @@ dependencies = [
|
||||
"codespan-reporting",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1170,7 +1149,7 @@ checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1193,7 +1172,7 @@ dependencies = [
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1204,7 +1183,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1224,6 +1203,18 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ddsfile"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479dfe1e6737aa9e96c6ac7b69689dc4c32da8383f2c12744739d76afa8b66c4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
"enum-primitive-derive",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
@@ -1245,7 +1236,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1270,7 +1261,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1312,6 +1303,17 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
|
||||
|
||||
[[package]]
|
||||
name = "enum-primitive-derive"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.10"
|
||||
@@ -1330,28 +1332,28 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d"
|
||||
checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293"
|
||||
dependencies = [
|
||||
"enumset_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset_derive"
|
||||
version = "0.8.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||
checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1466,7 +1468,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1575,7 +1577,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1795,6 +1797,19 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image_dds"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aeb76fb263814aa9dcc4370dda5b3c129ec9b711f5076ae584035c87db3ddc1f"
|
||||
dependencies = [
|
||||
"bcdec_rs",
|
||||
"bytemuck",
|
||||
"ddsfile",
|
||||
"half",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -1834,9 +1849,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "input-linux-sys"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a687a25a4973027df9153753a5589f97fe1e958f694a34eea5606ae65299ab8"
|
||||
checksum = "1c7ef95c35c8ef8d114f5e197a5ac9554dc4afdd19ae78ae1fd0fc0944cb1340"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"nix 0.26.4",
|
||||
@@ -1890,20 +1905,6 @@ version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
@@ -1928,9 +1929,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.31"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1990,19 +1991,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
@@ -2174,7 +2165,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2336,7 +2327,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2366,7 +2357,7 @@ dependencies = [
|
||||
"proc-macro-crate 2.0.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2596,7 +2587,7 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
|
||||
dependencies = [
|
||||
"jni 0.21.1",
|
||||
"jni",
|
||||
"ndk 0.8.0",
|
||||
"ndk-context",
|
||||
"num-derive",
|
||||
@@ -2621,23 +2612,20 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openxr"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7d5c194407c4fb5d3bf08c34ae57f3ea6cc9d9cfbe0594ce066896c809d9215"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/galister/openxrs?rev=af4a55d#af4a55df60125491c80c61464c824219c6019b76"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libloading 0.7.4",
|
||||
"libloading",
|
||||
"ndk-context",
|
||||
"openxr-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openxr-sys"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8f022053ecd7989d86f867b4fb8c3520347612b9d637e217077a0d6b4a6634"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/galister/openxrs?rev=af4a55d#af4a55df60125491c80c61464c824219c6019b76"
|
||||
dependencies = [
|
||||
"jni 0.19.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -2729,7 +2717,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.2",
|
||||
"redox_syscall 0.5.3",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
@@ -2786,7 +2774,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2855,7 +2843,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2943,7 +2931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3093,9 +3081,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
@@ -3259,14 +3247,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sctk-adwaita"
|
||||
version = "0.9.1"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7555fcb4f753d095d734fdefebb0ad8c98478a21db500492d87c55913d3b0086"
|
||||
checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"log",
|
||||
"memmap2 0.9.4",
|
||||
"smithay-client-toolkit 0.18.1",
|
||||
"smithay-client-toolkit",
|
||||
"tiny-skia",
|
||||
]
|
||||
|
||||
@@ -3293,7 +3281,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3326,7 +3314,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3444,39 +3432,14 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.18.1"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"calloop 0.12.4",
|
||||
"calloop-wayland-source 0.2.0",
|
||||
"cursor-icon",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap2 0.9.4",
|
||||
"rustix",
|
||||
"thiserror",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-csd-frame",
|
||||
"wayland-cursor",
|
||||
"wayland-protocols 0.31.2",
|
||||
"wayland-protocols-wlr 0.2.0",
|
||||
"wayland-scanner",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "837d3067369e24aeda699a5d9fc5aa14ca14a84dd70aeed7156bfa04a5605b32"
|
||||
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"bytemuck",
|
||||
"calloop 0.13.0",
|
||||
"calloop-wayland-source 0.3.0",
|
||||
"calloop",
|
||||
"calloop-wayland-source",
|
||||
"cursor-icon",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -3488,8 +3451,8 @@ dependencies = [
|
||||
"wayland-client",
|
||||
"wayland-csd-frame",
|
||||
"wayland-cursor",
|
||||
"wayland-protocols 0.32.2",
|
||||
"wayland-protocols-wlr 0.3.2",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-wlr",
|
||||
"wayland-scanner",
|
||||
"xkbcommon",
|
||||
"xkeysym",
|
||||
@@ -3541,7 +3504,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3557,9 +3520,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.70"
|
||||
version = "2.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3623,22 +3586,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3764,7 +3727,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3896,7 +3859,7 @@ dependencies = [
|
||||
"half",
|
||||
"heck 0.4.1",
|
||||
"indexmap 2.2.6",
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
"nom",
|
||||
"objc",
|
||||
"once_cell",
|
||||
@@ -3921,7 +3884,7 @@ dependencies = [
|
||||
"proc-macro-crate 2.0.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3934,7 +3897,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"shaderc",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
"vulkano",
|
||||
]
|
||||
|
||||
@@ -3975,7 +3938,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4009,7 +3972,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4022,9 +3985,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "269c04f203640d0da2092d1b8d89a2d081714ae3ac2f1b53e99f205740517198"
|
||||
checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
@@ -4036,9 +3999,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.4"
|
||||
version = "0.31.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08bd0f46c069d3382a36c8666c1b9ccef32b8b04f41667ca1fef06a1adcc2982"
|
||||
checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"rustix",
|
||||
@@ -4059,9 +4022,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-cursor"
|
||||
version = "0.31.4"
|
||||
version = "0.31.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09414bcf0fd8d9577d73e9ac4659ebc45bcc9cff1980a350543ad8e50ee263b2"
|
||||
checksum = "6ef9489a8df197ebf3a8ce8a7a7f0a2320035c3743f3c1bd0bdbccf07ce64f95"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"wayland-client",
|
||||
@@ -4070,21 +4033,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.31.2"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1794d82d869f38439d15c24b26f06f6c8603d27d47b4f786d5197c99044de415"
|
||||
checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
@@ -4094,48 +4045,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-plasma"
|
||||
version = "0.2.0"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
|
||||
checksum = "f79f2d57c7fcc6ab4d602adba364bf59a5c24de57bd194486bf9b8360e06bfc4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.31.2",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.2.0"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
||||
checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.31.2",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa43c961473aed713d44c1f616f775186249dfca657f256d8841ca0690366aba"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.32.2",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.3"
|
||||
version = "0.31.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edf466fc49a4feb65a511ca403fec3601494d0dee85dbf37fff6fa0dd4eec3b6"
|
||||
checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml 0.34.0",
|
||||
@@ -4144,9 +4082,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.3"
|
||||
version = "0.31.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a6754825230fa5b27bafaa28c30b3c9e72c55530581220cef401fa422c0fae7"
|
||||
checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"log",
|
||||
@@ -4460,9 +4398,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.3"
|
||||
version = "0.30.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f45a7b7e2de6af35448d7718dab6d95acec466eb3bb7a56f4d31d1af754004"
|
||||
checksum = "4225ddd8ab67b8b59a2fee4b34889ebf13c0460c1c3fa297c58e21eb87801b33"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"android-activity",
|
||||
@@ -4470,7 +4408,7 @@ dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"block2",
|
||||
"bytemuck",
|
||||
"calloop 0.12.4",
|
||||
"calloop",
|
||||
"cfg_aliases 0.2.1",
|
||||
"concurrent-queue",
|
||||
"core-foundation",
|
||||
@@ -4492,7 +4430,7 @@ dependencies = [
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix",
|
||||
"sctk-adwaita",
|
||||
"smithay-client-toolkit 0.18.1",
|
||||
"smithay-client-toolkit",
|
||||
"smol_str",
|
||||
"tracing",
|
||||
"unicode-segmentation",
|
||||
@@ -4500,7 +4438,7 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.31.2",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-plasma",
|
||||
"web-sys",
|
||||
"web-time",
|
||||
@@ -4532,9 +4470,9 @@ dependencies = [
|
||||
"once_cell",
|
||||
"pipewire",
|
||||
"rxscreen",
|
||||
"smithay-client-toolkit 0.19.1",
|
||||
"smithay-client-toolkit",
|
||||
"wayland-client",
|
||||
"wayland-protocols 0.32.2",
|
||||
"wayland-protocols",
|
||||
"xcb",
|
||||
]
|
||||
|
||||
@@ -4558,11 +4496,12 @@ dependencies = [
|
||||
"glam",
|
||||
"idmap",
|
||||
"idmap-derive",
|
||||
"image_dds",
|
||||
"input-linux",
|
||||
"json",
|
||||
"json5",
|
||||
"libc",
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
"log",
|
||||
"log-panics",
|
||||
"once_cell",
|
||||
@@ -4608,7 +4547,7 @@ dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"gethostname",
|
||||
"libc",
|
||||
"libloading 0.8.4",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"x11rb-protocol",
|
||||
@@ -4634,9 +4573,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911"
|
||||
checksum = "d491ee231a51ae64a5b762114c3ac2104b967aadba1de45c86ca42cf051513b7"
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
@@ -4811,7 +4750,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.70",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -33,7 +33,9 @@ libc = "0.2.155"
|
||||
libloading = "0.8.3"
|
||||
log = "0.4.21"
|
||||
once_cell = "1.19.0"
|
||||
openxr = { version = "0.17.1", features = ["linked"], optional = true }
|
||||
openxr = { git = "https://github.com/galister/openxrs", rev = "af4a55d", features = [
|
||||
"linked",
|
||||
], optional = true }
|
||||
ovr_overlay = { features = [
|
||||
"ovr_input",
|
||||
"ovr_system",
|
||||
@@ -62,6 +64,9 @@ xkbcommon = { version = "0.7.0" }
|
||||
xcb = { version = "1.4.0", optional = true, features = [
|
||||
"as-raw-xcb-connection",
|
||||
] }
|
||||
image_dds = { version = "0.5.1", default-features = false, features = [
|
||||
"ddsfile",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = ["openvr", "openxr", "osc", "x11", "wayland"]
|
||||
|
||||
@@ -32,7 +32,7 @@ impl LinePool {
|
||||
|
||||
let buf = vec![255; 16];
|
||||
|
||||
let texture = command_buffer.texture2d(2, 2, Format::R8G8B8A8_UNORM, &buf)?;
|
||||
let texture = command_buffer.texture2d_raw(2, 2, Format::R8G8B8A8_UNORM, &buf)?;
|
||||
command_buffer.build_and_execute_now()?;
|
||||
|
||||
graphics
|
||||
|
||||
@@ -35,6 +35,11 @@ pub(super) fn init_xr() -> Result<(xr::Instance, xr::SystemId), anyhow::Error> {
|
||||
} else {
|
||||
log::warn!("Missing EXT_hp_mixed_reality_controller extension.");
|
||||
}
|
||||
if available_extensions.khr_composition_layer_equirect2 {
|
||||
enabled_extensions.khr_composition_layer_equirect2 = true;
|
||||
} else {
|
||||
log::warn!("Missing EXT_composition_layer_equirect2 extension.");
|
||||
}
|
||||
|
||||
//#[cfg(not(debug_assertions))]
|
||||
let layers = [];
|
||||
|
||||
149
src/backend/openxr/libmonado.rs
Normal file
149
src/backend/openxr/libmonado.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::{env, ffi::c_void, fs};
|
||||
|
||||
use anyhow::bail;
|
||||
use libloading::{Library, Symbol};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug)]
|
||||
struct MndPose {
|
||||
orientation: [f32; 4],
|
||||
position: [f32; 3],
|
||||
}
|
||||
|
||||
const MND_REFERENCE_TYPE_STAGE: i32 = 3;
|
||||
|
||||
const MND_SUCCESS: i32 = 0;
|
||||
const MND_ERROR_BAD_SPACE_TYPE: i32 = -7;
|
||||
|
||||
type GetDeviceCount = extern "C" fn(*mut c_void, *mut u32) -> i32;
|
||||
type GetDeviceInfo = extern "C" fn(*mut c_void, u32, *mut u32, *mut *const char) -> i32;
|
||||
type GetDeviceFromRole = extern "C" fn(*mut c_void, *const std::os::raw::c_char, *mut i32) -> i32;
|
||||
type GetDeviceBatteryStatus =
|
||||
extern "C" fn(*mut c_void, u32, *mut bool, *mut bool, *mut f32) -> i32;
|
||||
|
||||
type PlaySpaceMove = extern "C" fn(*mut c_void, f32, f32, f32) -> i32;
|
||||
type ApplyStageOffset = extern "C" fn(*mut c_void, *const MndPose) -> i32;
|
||||
|
||||
// New implementation
|
||||
type GetReferenceSpaceOffset = extern "C" fn(*mut c_void, i32, *mut MndPose) -> i32;
|
||||
type SetReferenceSpaceOffset = extern "C" fn(*mut c_void, i32, *const MndPose) -> i32;
|
||||
|
||||
// TODO: Clean up after merge into upstream Monado
|
||||
enum MoverImpl {
|
||||
None,
|
||||
PlaySpaceMove(PlaySpaceMove),
|
||||
ApplyStageOffset(ApplyStageOffset),
|
||||
SpaceOffsetApi {
|
||||
get_reference: GetReferenceSpaceOffset,
|
||||
set_reference: SetReferenceSpaceOffset,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct LibMonado {
|
||||
libmonado: Library,
|
||||
mnd_root: *mut c_void,
|
||||
mover: MoverImpl,
|
||||
}
|
||||
|
||||
impl Drop for LibMonado {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
type RootDestroy = extern "C" fn(*mut *mut c_void) -> i32;
|
||||
let Ok(root_destroy) = self.libmonado.get::<RootDestroy>(b"mnd_root_destroy\0") else {
|
||||
return;
|
||||
};
|
||||
root_destroy(&mut self.mnd_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LibMonado {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let lib_path = if let Ok(path) = env::var("LIBMONADO_PATH") {
|
||||
path
|
||||
} else if let Some(path) = xr_runtime_manifest()
|
||||
.map(|manifest| manifest.runtime.mnd_libmonado_path)
|
||||
.ok()
|
||||
.flatten()
|
||||
{
|
||||
path
|
||||
} else {
|
||||
bail!("Monado: libmonado not found. Update your Monado/WiVRn or set LIBMONADO_PATH to point at your libmonado.so");
|
||||
};
|
||||
|
||||
let (libmonado, mnd_root) = unsafe {
|
||||
let libmonado = libloading::Library::new(lib_path)?;
|
||||
let root_create: Symbol<extern "C" fn(*mut *mut c_void) -> i32> =
|
||||
libmonado.get(b"mnd_root_create\0")?;
|
||||
|
||||
let mut root: *mut c_void = std::ptr::null_mut();
|
||||
let ret = root_create(&mut root);
|
||||
if ret != 0 {
|
||||
anyhow::bail!("Failed to create libmonado root, code: {}", ret);
|
||||
}
|
||||
|
||||
(libmonado, root)
|
||||
};
|
||||
|
||||
let space_api = unsafe {
|
||||
if let (Ok(get_reference), Ok(set_reference)) = (
|
||||
libmonado.get(b"mnd_root_get_reference_space_offset\0"),
|
||||
libmonado.get(b"mnd_root_set_reference_space_offset\0"),
|
||||
) {
|
||||
log::info!("Monado: using space offset API");
|
||||
|
||||
let get_reference: GetReferenceSpaceOffset = *get_reference;
|
||||
let set_reference: SetReferenceSpaceOffset = *set_reference;
|
||||
|
||||
MoverImpl::SpaceOffsetApi {
|
||||
get_reference,
|
||||
set_reference,
|
||||
}
|
||||
} else if let Ok(playspace_move) = libmonado.get(b"mnd_root_playspace_move\0") {
|
||||
log::warn!("Monado: using playspace_move, which is obsolete. Consider updating.");
|
||||
MoverImpl::PlaySpaceMove(*playspace_move)
|
||||
} else if let Ok(apply_stage_offset) = libmonado.get(b"mnd_root_apply_stage_offset\0") {
|
||||
log::warn!(
|
||||
"Monado: using apply_stage_offset, which is obsolete. Consider updating."
|
||||
);
|
||||
MoverImpl::ApplyStageOffset(*apply_stage_offset)
|
||||
} else {
|
||||
MoverImpl::None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
libmonado,
|
||||
mnd_root,
|
||||
mover: space_api,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn mover_supported(&self) -> bool {
|
||||
!matches!(self.mover, MoverImpl::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct XrRuntimeManifestRuntime {
|
||||
name: String,
|
||||
library_path: String,
|
||||
mnd_libmonado_path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct XrRuntimeManifest {
|
||||
file_format_version: String,
|
||||
runtime: XrRuntimeManifestRuntime,
|
||||
}
|
||||
|
||||
fn xr_runtime_manifest() -> anyhow::Result<XrRuntimeManifest> {
|
||||
let xdg_dirs = xdg::BaseDirectories::new()?; // only fails if $HOME unset
|
||||
let mut file = xdg_dirs.get_config_home();
|
||||
file.push("openxr/1/active_runtime.json");
|
||||
|
||||
let json = fs::read_to_string(file)?;
|
||||
let manifest = serde_json::from_str(&json)?;
|
||||
Ok(manifest)
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
||||
swapchain::{create_swapchain_render_data, SwapchainOpts, SwapchainRenderData},
|
||||
CompositionLayer, XrState,
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ impl LinePool {
|
||||
let views: anyhow::Result<Vec<Arc<ImageView>>> = colors
|
||||
.into_iter()
|
||||
.map(
|
||||
|color| match command_buffer.texture2d(1, 1, Format::R8G8B8A8_UNORM, &color) {
|
||||
|color| match command_buffer.texture2d_raw(1, 1, Format::R8G8B8A8_UNORM, &color) {
|
||||
Ok(tex) => ImageView::new_default(tex).map_err(|e| anyhow::anyhow!(e)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
@@ -68,7 +68,8 @@ impl LinePool {
|
||||
) -> anyhow::Result<usize> {
|
||||
let id = AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let srd = create_swapchain_render_data(xr, graphics, [1, 1, 1])?;
|
||||
let srd =
|
||||
create_swapchain_render_data(xr, graphics, [1, 1, 1], SwapchainOpts::new().srgb())?;
|
||||
self.lines.insert(
|
||||
id,
|
||||
LineContainer {
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::{
|
||||
|
||||
use glam::{Affine3A, Vec3};
|
||||
use openxr as xr;
|
||||
use skybox::create_skybox;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, Handle, VulkanObject};
|
||||
|
||||
use crate::{
|
||||
@@ -34,6 +35,7 @@ mod input;
|
||||
mod lines;
|
||||
mod overlay;
|
||||
mod playspace;
|
||||
mod skybox;
|
||||
mod swapchain;
|
||||
|
||||
const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO;
|
||||
@@ -122,6 +124,8 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
stage_offset: Affine3A::IDENTITY,
|
||||
};
|
||||
|
||||
let mut skybox = create_skybox(&xr_state, &app_state);
|
||||
|
||||
let pointer_lines = [
|
||||
lines.allocate(&xr_state, app_state.graphics.clone())?,
|
||||
lines.allocate(&xr_state, app_state.graphics.clone())?,
|
||||
@@ -136,6 +140,8 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
|
||||
let mut due_tasks = VecDeque::with_capacity(4);
|
||||
|
||||
let mut main_session_visible = false;
|
||||
|
||||
'main_loop: loop {
|
||||
let cur_frame = FRAME_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
@@ -179,6 +185,19 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
EventsLost(e) => {
|
||||
log::warn!("lost {} events", e.lost_event_count());
|
||||
}
|
||||
MainSessionVisibilityChangedEXTX(e) => {
|
||||
if main_session_visible != e.visible() {
|
||||
main_session_visible = e.visible();
|
||||
log::info!("Main session visible: {}", main_session_visible);
|
||||
if main_session_visible {
|
||||
log::debug!("Destroying skybox.");
|
||||
skybox = None;
|
||||
} else {
|
||||
log::debug!("Allocating skybox.");
|
||||
skybox = create_skybox(&xr_state, &app_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -284,6 +303,18 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
|
||||
if !main_session_visible {
|
||||
if let Some(skybox) = skybox.as_mut() {
|
||||
for (idx, layer) in skybox
|
||||
.present_xr(&xr_state, app_state.input_state.hmd, &mut command_buffer)?
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
layers.push((200.0 - 50.0 * (idx as f32), layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for o in overlays.iter_mut() {
|
||||
if !o.state.want_visible {
|
||||
continue;
|
||||
@@ -327,6 +358,7 @@ pub fn openxr_run(running: Arc<AtomicBool>, show_by_default: bool) -> Result<(),
|
||||
.map(|f| match f.1 {
|
||||
CompositionLayer::Quad(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||
CompositionLayer::Cylinder(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||
CompositionLayer::Equirect2(ref l) => l as &xr::CompositionLayerBase<xr::Vulkan>,
|
||||
CompositionLayer::None => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -413,4 +445,5 @@ pub(super) enum CompositionLayer<'a> {
|
||||
None,
|
||||
Quad(xr::CompositionLayerQuad<'a, xr::Vulkan>),
|
||||
Cylinder(xr::CompositionLayerCylinderKHR<'a, xr::Vulkan>),
|
||||
Equirect2(xr::CompositionLayerEquirect2KHR<'a, xr::Vulkan>),
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use glam::Vec3A;
|
||||
use openxr as xr;
|
||||
use openxr::{self as xr, CompositionLayerFlags};
|
||||
use std::{f32::consts::PI, sync::Arc};
|
||||
use xr::EyeVisibility;
|
||||
|
||||
use super::{helpers, swapchain::SwapchainRenderData, CompositionLayer, XrState};
|
||||
use crate::{
|
||||
backend::{openxr::swapchain::create_swapchain_render_data, overlay::OverlayData},
|
||||
backend::{
|
||||
openxr::swapchain::{create_swapchain_render_data, SwapchainOpts},
|
||||
overlay::OverlayData,
|
||||
},
|
||||
graphics::WlxCommandBuffer,
|
||||
state::AppState,
|
||||
};
|
||||
@@ -40,8 +43,12 @@ impl OverlayData<OpenXrOverlayData> {
|
||||
let data = match self.data.swapchain {
|
||||
Some(ref mut data) => data,
|
||||
None => {
|
||||
let srd =
|
||||
create_swapchain_render_data(xr, command_buffer.graphics.clone(), extent)?;
|
||||
let srd = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
extent,
|
||||
SwapchainOpts::new(),
|
||||
)?;
|
||||
log::debug!(
|
||||
"{}: Created swapchain {}x{}, {} images, {} MB",
|
||||
self.state.name,
|
||||
@@ -75,6 +82,7 @@ impl OverlayData<OpenXrOverlayData> {
|
||||
let angle = 2.0 * (scale_x / (2.0 * radius));
|
||||
|
||||
let cylinder = xr::CompositionLayerCylinderKHR::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(posef)
|
||||
.sub_image(sub_image)
|
||||
.eye_visibility(EyeVisibility::BOTH)
|
||||
@@ -86,6 +94,7 @@ impl OverlayData<OpenXrOverlayData> {
|
||||
} else {
|
||||
let posef = helpers::transform_to_posef(&self.state.transform);
|
||||
let quad = xr::CompositionLayerQuad::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(posef)
|
||||
.sub_image(sub_image)
|
||||
.eye_visibility(EyeVisibility::BOTH)
|
||||
|
||||
168
src/backend/openxr/skybox.rs
Normal file
168
src/backend/openxr/skybox.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use std::{f32::consts::PI, fs::File, sync::Arc};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use once_cell::sync::Lazy;
|
||||
use openxr::{self as xr, CompositionLayerFlags};
|
||||
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
|
||||
|
||||
use crate::{
|
||||
backend::openxr::{helpers::translation_rotation_to_posef, swapchain::SwapchainOpts},
|
||||
config_io::CONFIG_ROOT_PATH,
|
||||
graphics::{dds::WlxCommandBufferDds, format_is_srgb, WlxCommandBuffer},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::{
|
||||
swapchain::{create_swapchain_render_data, SwapchainRenderData},
|
||||
CompositionLayer, XrState,
|
||||
};
|
||||
|
||||
pub(super) struct Skybox {
|
||||
view: Arc<ImageView>,
|
||||
srd: Option<(SwapchainRenderData, SwapchainRenderData)>,
|
||||
}
|
||||
|
||||
impl Skybox {
|
||||
pub fn new(app: &AppState) -> anyhow::Result<Self> {
|
||||
let mut command_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
|
||||
let mut maybe_image = None;
|
||||
|
||||
'custom_tex: {
|
||||
if app.session.config.skybox_texture.is_empty() {
|
||||
break 'custom_tex;
|
||||
}
|
||||
|
||||
let real_path = CONFIG_ROOT_PATH.join(&*app.session.config.skybox_texture);
|
||||
let Ok(f) = File::open(real_path) else {
|
||||
log::warn!(
|
||||
"Could not open custom skybox texture at: {}",
|
||||
app.session.config.skybox_texture
|
||||
);
|
||||
break 'custom_tex;
|
||||
};
|
||||
match command_buffer.texture2d_dds(f) {
|
||||
Ok(image) => {
|
||||
maybe_image = Some(image);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not use custom skybox texture at: {}",
|
||||
app.session.config.skybox_texture
|
||||
);
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if maybe_image.is_none() {
|
||||
let p = include_bytes!("../../res/table_mountain_2.dds");
|
||||
maybe_image = Some(command_buffer.texture2d_dds(p.as_slice())?);
|
||||
}
|
||||
|
||||
command_buffer.build_and_execute_now()?;
|
||||
|
||||
let view = ImageView::new_default(maybe_image.unwrap())?; // safe unwrap
|
||||
|
||||
Ok(Self { view, srd: None })
|
||||
}
|
||||
|
||||
pub(super) fn present_xr<'a>(
|
||||
&'a mut self,
|
||||
xr: &'a XrState,
|
||||
hmd: Affine3A,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<Vec<CompositionLayer>> {
|
||||
let (sky_image, grid_image) = if let Some((ref mut srd_sky, ref mut srd_grid)) = self.srd {
|
||||
(srd_sky.present_last()?, srd_grid.present_last()?)
|
||||
} else {
|
||||
log::debug!("Render skybox.");
|
||||
|
||||
let mut opts = SwapchainOpts::new().immutable();
|
||||
opts.srgb = format_is_srgb(self.view.image().format());
|
||||
|
||||
let srd_sky = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
self.view.image().extent(),
|
||||
opts,
|
||||
)?;
|
||||
|
||||
let srd_grid = create_swapchain_render_data(
|
||||
xr,
|
||||
command_buffer.graphics.clone(),
|
||||
[1024, 1024, 1],
|
||||
SwapchainOpts::new().immutable().grid(),
|
||||
)?;
|
||||
|
||||
self.srd = Some((srd_sky, srd_grid));
|
||||
|
||||
let (srd_sky, srd_grid) = self.srd.as_mut().unwrap(); // safe unwrap
|
||||
|
||||
(
|
||||
srd_sky.acquire_present_release(command_buffer, self.view.clone(), 1.0)?,
|
||||
srd_grid.acquire_compute_release(command_buffer)?,
|
||||
)
|
||||
};
|
||||
|
||||
let pose = xr::Posef {
|
||||
orientation: xr::Quaternionf::IDENTITY,
|
||||
position: xr::Vector3f {
|
||||
x: hmd.translation.x,
|
||||
y: hmd.translation.y,
|
||||
z: hmd.translation.z,
|
||||
},
|
||||
};
|
||||
|
||||
// cover the entire sphere
|
||||
const HORIZ_ANGLE: f32 = 2.0 * PI;
|
||||
const HI_VERT_ANGLE: f32 = 0.5 * PI;
|
||||
const LO_VERT_ANGLE: f32 = -0.5 * PI;
|
||||
|
||||
let mut layers = vec![];
|
||||
|
||||
let sky = xr::CompositionLayerEquirect2KHR::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(pose)
|
||||
.radius(10.0)
|
||||
.sub_image(sky_image)
|
||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||
.space(&xr.stage)
|
||||
.central_horizontal_angle(HORIZ_ANGLE)
|
||||
.upper_vertical_angle(HI_VERT_ANGLE)
|
||||
.lower_vertical_angle(LO_VERT_ANGLE);
|
||||
|
||||
layers.push(CompositionLayer::Equirect2(sky));
|
||||
|
||||
static GRID_POSE: Lazy<xr::Posef> = Lazy::new(|| {
|
||||
translation_rotation_to_posef(Vec3A::ZERO, Quat::from_rotation_x(PI * -0.5))
|
||||
});
|
||||
|
||||
let grid = xr::CompositionLayerQuad::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.pose(*GRID_POSE)
|
||||
.size(xr::Extent2Df {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
})
|
||||
.sub_image(grid_image)
|
||||
.eye_visibility(xr::EyeVisibility::BOTH)
|
||||
.space(&xr.stage);
|
||||
|
||||
layers.push(CompositionLayer::Quad(grid));
|
||||
|
||||
Ok(layers)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn create_skybox(xr: &XrState, app: &AppState) -> Option<Skybox> {
|
||||
if !app.session.config.use_skybox {
|
||||
return None;
|
||||
}
|
||||
xr.instance
|
||||
.exts()
|
||||
.khr_composition_layer_equirect2
|
||||
.and_then(|_| Skybox::new(app).ok())
|
||||
}
|
||||
@@ -16,13 +16,45 @@ use crate::graphics::{WlxCommandBuffer, WlxGraphics, WlxPipeline, WlxPipelineDyn
|
||||
|
||||
use super::XrState;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct SwapchainOpts {
|
||||
pub immutable: bool,
|
||||
pub srgb: bool,
|
||||
pub grid: bool,
|
||||
}
|
||||
|
||||
impl SwapchainOpts {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
pub fn immutable(mut self) -> Self {
|
||||
self.immutable = true;
|
||||
self
|
||||
}
|
||||
pub fn srgb(mut self) -> Self {
|
||||
self.srgb = true;
|
||||
self
|
||||
}
|
||||
pub fn grid(mut self) -> Self {
|
||||
self.grid = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn create_swapchain_render_data(
|
||||
xr: &XrState,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
extent: [u32; 3],
|
||||
opts: SwapchainOpts,
|
||||
) -> anyhow::Result<SwapchainRenderData> {
|
||||
let create_flags = if opts.immutable {
|
||||
xr::SwapchainCreateFlags::STATIC_IMAGE
|
||||
} else {
|
||||
xr::SwapchainCreateFlags::EMPTY
|
||||
};
|
||||
|
||||
let swapchain = xr.session.create_swapchain(&xr::SwapchainCreateInfo {
|
||||
create_flags: xr::SwapchainCreateFlags::EMPTY,
|
||||
create_flags,
|
||||
usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT | xr::SwapchainUsageFlags::SAMPLED,
|
||||
format: Format::R8G8B8A8_SRGB as _,
|
||||
sample_count: 1,
|
||||
@@ -36,10 +68,22 @@ pub(super) fn create_swapchain_render_data(
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let image_fmt = if opts.srgb {
|
||||
Format::R8G8B8A8_SRGB
|
||||
} else {
|
||||
Format::R8G8B8A8_UNORM
|
||||
};
|
||||
|
||||
let frag_shader = if opts.grid {
|
||||
"frag_grid"
|
||||
} else {
|
||||
"frag_swapchain"
|
||||
};
|
||||
let pipeline = graphics.create_pipeline_dynamic(
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_swapchain").unwrap().clone(), // want panic
|
||||
Format::R8G8B8A8_UNORM,
|
||||
shaders.get(frag_shader).unwrap().clone(), // want panic
|
||||
image_fmt,
|
||||
Some(AttachmentBlend::alpha()),
|
||||
)?;
|
||||
|
||||
@@ -54,7 +98,7 @@ pub(super) fn create_swapchain_render_data(
|
||||
graphics.device.clone(),
|
||||
vk_image,
|
||||
ImageCreateInfo {
|
||||
format: Format::R8G8B8A8_UNORM, // actually SRGB but we lie
|
||||
format: image_fmt, // actually SRGB but we lie
|
||||
extent,
|
||||
usage: ImageUsage::COLOR_ATTACHMENT,
|
||||
..Default::default()
|
||||
@@ -72,6 +116,7 @@ pub(super) fn create_swapchain_render_data(
|
||||
pipeline,
|
||||
images,
|
||||
extent,
|
||||
target_extent: [0, 0, 0],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,6 +124,7 @@ pub(super) struct SwapchainRenderData {
|
||||
pub(super) swapchain: xr::Swapchain<xr::Vulkan>,
|
||||
pub(super) pipeline: Arc<WlxPipeline<WlxPipelineDynamic>>,
|
||||
pub(super) extent: [u32; 3],
|
||||
pub(super) target_extent: [u32; 3],
|
||||
pub(super) images: SmallVec<[Arc<ImageView>; 4]>,
|
||||
}
|
||||
|
||||
@@ -95,7 +141,7 @@ impl SwapchainRenderData {
|
||||
let render_target = &mut self.images[idx];
|
||||
command_buffer.begin_rendering(render_target.clone())?;
|
||||
|
||||
let target_extent = render_target.image().extent();
|
||||
self.target_extent = render_target.image().extent();
|
||||
|
||||
let set0 = self.pipeline.uniform_sampler(
|
||||
0,
|
||||
@@ -106,7 +152,7 @@ impl SwapchainRenderData {
|
||||
let set1 = self.pipeline.uniform_buffer(1, vec![alpha])?;
|
||||
|
||||
let pass = self.pipeline.create_pass(
|
||||
[target_extent[0] as _, target_extent[1] as _],
|
||||
[self.target_extent[0] as _, self.target_extent[1] as _],
|
||||
command_buffer.graphics.quad_verts.clone(),
|
||||
command_buffer.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
@@ -121,8 +167,60 @@ impl SwapchainRenderData {
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: target_extent[0] as _,
|
||||
height: target_extent[1] as _,
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
}
|
||||
|
||||
pub(super) fn acquire_compute_release(
|
||||
&mut self,
|
||||
command_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
|
||||
let idx = self.swapchain.acquire_image()? as usize;
|
||||
self.swapchain.wait_image(xr::Duration::INFINITE)?;
|
||||
|
||||
let render_target = &mut self.images[idx];
|
||||
command_buffer.begin_rendering(render_target.clone())?;
|
||||
|
||||
self.target_extent = render_target.image().extent();
|
||||
|
||||
let pass = self.pipeline.create_pass(
|
||||
[self.target_extent[0] as _, self.target_extent[1] as _],
|
||||
command_buffer.graphics.quad_verts.clone(),
|
||||
command_buffer.graphics.quad_indices.clone(),
|
||||
vec![],
|
||||
)?;
|
||||
command_buffer.run_ref(&pass)?;
|
||||
command_buffer.end_rendering()?;
|
||||
|
||||
self.swapchain.release_image()?;
|
||||
|
||||
Ok(xr::SwapchainSubImage::new()
|
||||
.swapchain(&self.swapchain)
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
}
|
||||
|
||||
pub(super) fn present_last(&self) -> anyhow::Result<xr::SwapchainSubImage<xr::Vulkan>> {
|
||||
debug_assert!(
|
||||
self.target_extent[0] * self.target_extent[1] != 0,
|
||||
"present_last: target_extent zero"
|
||||
);
|
||||
Ok(xr::SwapchainSubImage::new()
|
||||
.swapchain(&self.swapchain)
|
||||
.image_rect(xr::Rect2Di {
|
||||
offset: xr::Offset2Di { x: 0, y: 0 },
|
||||
extent: xr::Extent2Di {
|
||||
width: self.target_extent[0] as _,
|
||||
height: self.target_extent[1] as _,
|
||||
},
|
||||
})
|
||||
.image_array_index(0))
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
image::{sampler::Filter, view::ImageView, ImageUsage},
|
||||
image::{view::ImageView, ImageUsage},
|
||||
swapchain::{
|
||||
acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo,
|
||||
},
|
||||
@@ -21,8 +21,8 @@ use crate::{
|
||||
config_io,
|
||||
graphics::{DynamicPass, DynamicPipeline, WlxGraphics, BLEND_ALPHA},
|
||||
gui::{
|
||||
canvas::Canvas,
|
||||
modular::{modular_canvas, ModularData},
|
||||
Canvas,
|
||||
},
|
||||
hid::USE_UINPUT,
|
||||
state::{AppState, ScreenMeta},
|
||||
|
||||
@@ -141,6 +141,10 @@ fn def_auto() -> Arc<str> {
|
||||
"auto".into()
|
||||
}
|
||||
|
||||
fn def_empty() -> Arc<str> {
|
||||
"".into()
|
||||
}
|
||||
|
||||
fn def_toast_topics() -> IdMap<ToastTopic, DisplayMethod> {
|
||||
IdMap::new()
|
||||
}
|
||||
@@ -243,6 +247,12 @@ pub struct GeneralConfig {
|
||||
|
||||
#[serde(default = "def_one")]
|
||||
pub space_drag_multiplier: f32,
|
||||
|
||||
#[serde(default = "def_empty")]
|
||||
pub skybox_texture: Arc<str>,
|
||||
|
||||
#[serde(default = "def_true")]
|
||||
pub use_skybox: bool,
|
||||
}
|
||||
|
||||
impl GeneralConfig {
|
||||
|
||||
102
src/graphics/dds.rs
Normal file
102
src/graphics/dds.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use image_dds::{ImageFormat, Surface};
|
||||
use std::{io::Read, sync::Arc};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||
command_buffer::CopyBufferToImageInfo,
|
||||
format::Format,
|
||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||
DeviceSize,
|
||||
};
|
||||
|
||||
use super::WlxCommandBuffer;
|
||||
|
||||
pub trait WlxCommandBufferDds {
|
||||
fn texture2d_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
where
|
||||
R: Read;
|
||||
}
|
||||
|
||||
impl WlxCommandBufferDds for WlxCommandBuffer {
|
||||
fn texture2d_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let Ok(dds) = image_dds::ddsfile::Dds::read(r) else {
|
||||
anyhow::bail!("Not a valid DDS file.\nSee: https://github.com/galister/wlx-overlay-s/wiki/Custom-Textures");
|
||||
};
|
||||
|
||||
let surface = Surface::from_dds(&dds)?;
|
||||
|
||||
if surface.depth != 1 {
|
||||
anyhow::bail!("Not a 2D texture.")
|
||||
}
|
||||
|
||||
let image = Image::new(
|
||||
self.graphics.memory_allocator.clone(),
|
||||
ImageCreateInfo {
|
||||
image_type: ImageType::Dim2d,
|
||||
format: dds_to_vk(surface.image_format)?,
|
||||
extent: [surface.width, surface.height, surface.depth],
|
||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::TRANSFER_SRC | ImageUsage::SAMPLED,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo::default(),
|
||||
)?;
|
||||
|
||||
let buffer: Subbuffer<[u8]> = Buffer::new_slice(
|
||||
self.graphics.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::TRANSFER_SRC,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
surface.data.len() as DeviceSize,
|
||||
)?;
|
||||
|
||||
buffer.write()?.copy_from_slice(surface.data);
|
||||
|
||||
self.command_buffer
|
||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(buffer, image.clone()))?;
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dds_to_vk(dds_fmt: ImageFormat) -> anyhow::Result<Format> {
|
||||
match dds_fmt {
|
||||
ImageFormat::R8Unorm => Ok(Format::R8_UNORM),
|
||||
ImageFormat::Rgba8Unorm => Ok(Format::R8G8B8A8_UNORM),
|
||||
ImageFormat::Rgba8UnormSrgb => Ok(Format::R8G8B8A8_SRGB),
|
||||
ImageFormat::Rgba16Float => Ok(Format::R16G16B16A16_SFLOAT),
|
||||
ImageFormat::Rgba32Float => Ok(Format::R32G32B32A32_SFLOAT),
|
||||
ImageFormat::Bgra8Unorm => Ok(Format::B8G8R8A8_UNORM),
|
||||
ImageFormat::Bgra8UnormSrgb => Ok(Format::B8G8R8A8_SRGB),
|
||||
// DXT1
|
||||
ImageFormat::BC1RgbaUnorm => Ok(Format::BC1_RGBA_UNORM_BLOCK),
|
||||
ImageFormat::BC1RgbaUnormSrgb => Ok(Format::BC1_RGBA_SRGB_BLOCK),
|
||||
// DXT3
|
||||
ImageFormat::BC2RgbaUnorm => Ok(Format::BC2_UNORM_BLOCK),
|
||||
ImageFormat::BC2RgbaUnormSrgb => Ok(Format::BC2_SRGB_BLOCK),
|
||||
// DXT5
|
||||
ImageFormat::BC3RgbaUnorm => Ok(Format::BC3_UNORM_BLOCK),
|
||||
ImageFormat::BC3RgbaUnormSrgb => Ok(Format::BC3_SRGB_BLOCK),
|
||||
// RGTC1
|
||||
ImageFormat::BC4RUnorm => Ok(Format::BC4_UNORM_BLOCK),
|
||||
ImageFormat::BC4RSnorm => Ok(Format::BC4_SNORM_BLOCK),
|
||||
// RGTC2
|
||||
ImageFormat::BC5RgUnorm => Ok(Format::BC5_UNORM_BLOCK),
|
||||
ImageFormat::BC5RgSnorm => Ok(Format::BC5_SNORM_BLOCK),
|
||||
// BPTC
|
||||
ImageFormat::BC6hRgbUfloat => Ok(Format::BC6H_UFLOAT_BLOCK),
|
||||
ImageFormat::BC6hRgbSfloat => Ok(Format::BC6H_SFLOAT_BLOCK),
|
||||
// BPTC
|
||||
ImageFormat::BC7RgbaUnorm => Ok(Format::BC7_UNORM_BLOCK),
|
||||
ImageFormat::BC7RgbaUnormSrgb => Ok(Format::BC7_SRGB_BLOCK),
|
||||
_ => anyhow::bail!("Unsupported format {:?}", dds_fmt),
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub(crate) mod dds;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
os::fd::{FromRawFd, IntoRawFd},
|
||||
@@ -1083,7 +1085,7 @@ impl WlxCommandBuffer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn texture2d(
|
||||
pub fn texture2d_raw(
|
||||
&mut self,
|
||||
width: u32,
|
||||
height: u32,
|
||||
@@ -1657,3 +1659,15 @@ fn memory_allocator(device: Arc<Device>) -> Arc<StandardMemoryAllocator> {
|
||||
|
||||
Arc::new(StandardMemoryAllocator::new(device, create_info))
|
||||
}
|
||||
|
||||
pub fn format_is_srgb(format: Format) -> bool {
|
||||
matches!(
|
||||
format,
|
||||
Format::R8G8B8A8_SRGB
|
||||
| Format::B8G8R8A8_SRGB
|
||||
| Format::BC1_RGBA_SRGB_BLOCK
|
||||
| Format::BC2_SRGB_BLOCK
|
||||
| Format::BC3_SRGB_BLOCK
|
||||
| Format::BC7_SRGB_BLOCK
|
||||
)
|
||||
}
|
||||
240
src/gui/canvas/builder.rs
Normal file
240
src/gui/canvas/builder.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
use glam::Vec4;
|
||||
use std::sync::Arc;
|
||||
|
||||
use vulkano::format::Format;
|
||||
|
||||
use crate::{
|
||||
graphics::WlxGraphics,
|
||||
gui::{canvas::control::ControlRenderer, GuiColor, KeyCapType},
|
||||
};
|
||||
|
||||
use super::{control::Control, Canvas, Rect};
|
||||
|
||||
pub struct CanvasBuilder<D, S> {
|
||||
canvas: Canvas<D, S>,
|
||||
|
||||
pub fg_color: GuiColor,
|
||||
pub bg_color: GuiColor,
|
||||
pub font_size: isize,
|
||||
}
|
||||
|
||||
impl<D, S> CanvasBuilder<D, S> {
|
||||
pub fn new(
|
||||
width: usize,
|
||||
height: usize,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
format: Format,
|
||||
data: D,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
canvas: Canvas::new(width, height, graphics, format, data)?,
|
||||
bg_color: Vec4::ZERO,
|
||||
fg_color: Vec4::ONE,
|
||||
font_size: 16,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build(self) -> Canvas<D, S> {
|
||||
self.canvas
|
||||
}
|
||||
|
||||
// Creates a panel with bg_color inherited from the canvas
|
||||
pub fn panel(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
bg_color: self.bg_color,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a label with fg_color, font_size inherited from the canvas
|
||||
pub fn label(&mut self, x: f32, y: f32, w: f32, h: f32, text: Arc<str>) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(Control::render_text),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a label with fg_color, font_size inherited from the canvas
|
||||
#[allow(dead_code)]
|
||||
pub fn label_centered(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
text: Arc<str>,
|
||||
) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(Control::render_text_centered),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a sprite. Will not draw anything until set_sprite is called.
|
||||
pub fn sprite(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
on_render_bg: Some(Control::render_sprite_bg),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a sprite that highlights on pointer hover. Will not draw anything until set_sprite is called.
|
||||
pub fn sprite_interactive(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
on_render_bg: Some(Control::render_sprite_bg),
|
||||
on_render_hl: Some(Control::render_sprite_hl),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a button with fg_color, bg_color, font_size inherited from the canvas
|
||||
pub fn button(&mut self, x: f32, y: f32, w: f32, h: f32, text: Arc<str>) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
|
||||
self.canvas.interactive_set_idx(x, y, w, h, idx);
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
bg_color: self.bg_color,
|
||||
size: self.font_size,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
on_render_fg: Some(Control::render_text_centered),
|
||||
on_render_hl: Some(Control::render_highlight),
|
||||
..Control::new()
|
||||
});
|
||||
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
pub fn key_button(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
cap_type: KeyCapType,
|
||||
label: &[String],
|
||||
) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.interactive_set_idx(x, y, w, h, idx);
|
||||
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
bg_color: self.bg_color,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
on_render_hl: Some(Control::render_highlight),
|
||||
..Control::new()
|
||||
});
|
||||
|
||||
let renders = match cap_type {
|
||||
KeyCapType::Regular => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||
let rect = Rect {
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h: h - self.font_size as f32,
|
||||
};
|
||||
vec![(render, rect, 1f32)]
|
||||
}
|
||||
KeyCapType::RegularAltGr => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text;
|
||||
let rect0 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + (self.font_size as f32) + 12.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x: x + w * 0.5 + 12.,
|
||||
y: y + h - (self.font_size as f32) + 8.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
vec![(render, rect0, 1.0), (render, rect1, 0.8)]
|
||||
}
|
||||
KeyCapType::Reversed => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||
let rect0 = Rect {
|
||||
x,
|
||||
y: y + 2.0,
|
||||
w,
|
||||
h: h * 0.5,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x,
|
||||
y: y + h * 0.5 + 2.0,
|
||||
w,
|
||||
h: h * 0.5,
|
||||
};
|
||||
vec![(render, rect1, 1.0), (render, rect0, 0.8)]
|
||||
}
|
||||
KeyCapType::ReversedAltGr => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text;
|
||||
let rect0 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + (self.font_size as f32) + 8.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + h - (self.font_size as f32) + 4.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect2 = Rect {
|
||||
x: x + w * 0.5 + 8.,
|
||||
y: y + h - (self.font_size as f32) + 4.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
vec![
|
||||
(render, rect1, 1.0),
|
||||
(render, rect0, 0.8),
|
||||
(render, rect2, 0.8),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
for (idx, (render, rect, alpha)) in renders.into_iter().enumerate() {
|
||||
if idx >= label.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
self.canvas.controls.push(Control {
|
||||
rect,
|
||||
text: Arc::from(label[idx].as_str()),
|
||||
fg_color: self.fg_color * alpha,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(render),
|
||||
..Control::new()
|
||||
});
|
||||
}
|
||||
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
}
|
||||
352
src/gui/canvas/control.rs
Normal file
352
src/gui/canvas/control.rs
Normal file
@@ -0,0 +1,352 @@
|
||||
use glam::Vec4;
|
||||
use std::sync::Arc;
|
||||
use vulkano::image::view::ImageView;
|
||||
|
||||
use crate::{
|
||||
backend::input::PointerMode, graphics::WlxCommandBuffer, gui::GuiColor, state::AppState,
|
||||
};
|
||||
|
||||
use super::{CanvasData, Rect};
|
||||
|
||||
pub type ControlRenderer<D, S> =
|
||||
fn(&Control<D, S>, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>;
|
||||
|
||||
pub type ControlRendererHl<D, S> = fn(
|
||||
&Control<D, S>,
|
||||
&CanvasData<D>,
|
||||
&mut AppState,
|
||||
&mut WlxCommandBuffer,
|
||||
Vec4,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
pub(crate) struct Control<D, S> {
|
||||
pub state: Option<S>,
|
||||
pub rect: Rect,
|
||||
pub fg_color: GuiColor,
|
||||
pub bg_color: GuiColor,
|
||||
pub text: Arc<str>,
|
||||
pub size: isize,
|
||||
pub sprite: Option<Arc<ImageView>>,
|
||||
pub sprite_st: Vec4,
|
||||
pub(super) dirty: bool,
|
||||
|
||||
pub on_update: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_press: Option<fn(&mut Self, &mut D, &mut AppState, PointerMode)>,
|
||||
pub on_release: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32)>,
|
||||
pub test_highlight: Option<fn(&Self, &mut D, &mut AppState) -> Option<Vec4>>,
|
||||
|
||||
pub(super) on_render_bg: Option<ControlRenderer<D, S>>,
|
||||
pub(super) on_render_hl: Option<ControlRendererHl<D, S>>,
|
||||
pub(super) on_render_fg: Option<ControlRenderer<D, S>>,
|
||||
}
|
||||
|
||||
impl<D, S> Control<D, S> {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
rect: Rect {
|
||||
x: 0.,
|
||||
y: 0.,
|
||||
w: 0.,
|
||||
h: 0.,
|
||||
},
|
||||
fg_color: Vec4::ONE,
|
||||
bg_color: Vec4::ZERO,
|
||||
text: Arc::from(""),
|
||||
sprite: None,
|
||||
sprite_st: Vec4::new(1., 1., 0., 0.),
|
||||
dirty: true,
|
||||
size: 24,
|
||||
state: None,
|
||||
on_update: None,
|
||||
on_render_bg: None,
|
||||
on_render_hl: None,
|
||||
on_render_fg: None,
|
||||
test_highlight: None,
|
||||
on_press: None,
|
||||
on_release: None,
|
||||
on_scroll: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
if *self.text == *text {
|
||||
return;
|
||||
}
|
||||
self.text = text.into();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_sprite(&mut self, sprite: Arc<ImageView>) {
|
||||
self.sprite.replace(sprite);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_sprite_st(&mut self, sprite_st: Vec4) {
|
||||
if self.sprite_st == sprite_st {
|
||||
return;
|
||||
}
|
||||
self.sprite_st = sprite_st;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_fg_color(&mut self, color: GuiColor) {
|
||||
if self.fg_color == color {
|
||||
return;
|
||||
}
|
||||
self.fg_color = color;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub(super) fn render_rect(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
_: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let pass = {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
let set0 = canvas
|
||||
.pipeline_bg_color
|
||||
.uniform_buffer(0, self.bg_color.to_array().to_vec())?;
|
||||
canvas.pipeline_bg_color.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)?
|
||||
};
|
||||
|
||||
cmd_buffer.run_ref(&pass)
|
||||
}
|
||||
|
||||
pub(super) fn render_highlight(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
_: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
color: GuiColor,
|
||||
) -> anyhow::Result<()> {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
|
||||
let set0 = canvas
|
||||
.pipeline_bg_color
|
||||
.uniform_buffer(0, color.to_array().to_vec())?;
|
||||
|
||||
let pass = canvas.pipeline_bg_color.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer.clone(),
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)?;
|
||||
|
||||
cmd_buffer.run_ref(&pass)
|
||||
}
|
||||
|
||||
pub(super) fn render_text(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut cur_y = self.rect.y;
|
||||
for line in self.text.lines() {
|
||||
let mut cur_x = self.rect.x;
|
||||
for glyph in app
|
||||
.fc
|
||||
.get_glyphs(line, self.size, canvas.graphics.clone())?
|
||||
{
|
||||
if let Some(tex) = glyph.tex.clone() {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
cur_x + glyph.left,
|
||||
cur_y - glyph.top,
|
||||
glyph.width,
|
||||
glyph.height,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_fg_glyph.uniform_sampler(
|
||||
0,
|
||||
ImageView::new_default(tex)?,
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set1 = canvas
|
||||
.pipeline_fg_glyph
|
||||
.uniform_buffer(1, self.fg_color.to_array().to_vec())?;
|
||||
let pass = canvas.pipeline_fg_glyph.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
}
|
||||
cur_x += glyph.advance;
|
||||
}
|
||||
cur_y += (self.size as f32) * 1.5;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn render_text_centered(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let (w, h) = app
|
||||
.fc
|
||||
.get_text_size(&self.text, self.size, canvas.graphics.clone())?;
|
||||
|
||||
let mut cur_y = self.rect.y + (self.rect.h) - (h * 0.5) - (self.size as f32 * 0.25);
|
||||
for line in self.text.lines() {
|
||||
let mut cur_x = self.rect.x + (self.rect.w * 0.5) - (w * 0.5);
|
||||
for glyph in app
|
||||
.fc
|
||||
.get_glyphs(line, self.size, canvas.graphics.clone())?
|
||||
{
|
||||
if let Some(tex) = glyph.tex.clone() {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
cur_x + glyph.left,
|
||||
cur_y - glyph.top,
|
||||
glyph.width,
|
||||
glyph.height,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_fg_glyph.uniform_sampler(
|
||||
0,
|
||||
ImageView::new_default(tex)?,
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set1 = canvas
|
||||
.pipeline_fg_glyph
|
||||
.uniform_buffer(1, self.fg_color.to_array().to_vec())?;
|
||||
let pass = canvas.pipeline_fg_glyph.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
}
|
||||
cur_x += glyph.advance;
|
||||
}
|
||||
cur_y += (self.size as f32) * 1.5;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn render_sprite_bg(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let Some(view) = self.sprite.as_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_bg_sprite.uniform_sampler(
|
||||
0,
|
||||
view.clone(),
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
|
||||
let uniform = vec![
|
||||
self.sprite_st.x,
|
||||
self.sprite_st.y,
|
||||
self.sprite_st.z,
|
||||
self.sprite_st.w,
|
||||
self.fg_color.x,
|
||||
self.fg_color.y,
|
||||
self.fg_color.z,
|
||||
self.fg_color.w,
|
||||
];
|
||||
|
||||
let set1 = canvas.pipeline_bg_sprite.uniform_buffer(1, uniform)?;
|
||||
|
||||
let pass = canvas.pipeline_bg_sprite.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn render_sprite_hl(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
color: GuiColor,
|
||||
) -> anyhow::Result<()> {
|
||||
let Some(view) = self.sprite.as_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_hl_sprite.uniform_sampler(
|
||||
0,
|
||||
view.clone(),
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
|
||||
let uniform = vec![
|
||||
self.sprite_st.x,
|
||||
self.sprite_st.y,
|
||||
self.sprite_st.z,
|
||||
self.sprite_st.w,
|
||||
color.x,
|
||||
color.y,
|
||||
color.z,
|
||||
color.w,
|
||||
];
|
||||
|
||||
let set1 = canvas.pipeline_hl_sprite.uniform_buffer(1, uniform)?;
|
||||
|
||||
let pass = canvas.pipeline_hl_sprite.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
378
src/gui/canvas/mod.rs
Normal file
378
src/gui/canvas/mod.rs
Normal file
@@ -0,0 +1,378 @@
|
||||
pub(crate) mod builder;
|
||||
pub(crate) mod control;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use glam::{Vec2, Vec4};
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
format::Format,
|
||||
image::{view::ImageView, ImageLayout},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{Haptics, InteractionHandler, PointerHit},
|
||||
overlay::{OverlayBackend, OverlayRenderer},
|
||||
},
|
||||
graphics::{WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy, BLEND_ALPHA},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
const RES_DIVIDER: usize = 4;
|
||||
|
||||
pub struct Rect {
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
}
|
||||
|
||||
pub struct CanvasData<D> {
|
||||
pub data: D,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
|
||||
graphics: Arc<WlxGraphics>,
|
||||
|
||||
pipeline_bg_color: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_fg_glyph: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_bg_sprite: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_hl_sprite: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_final: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
}
|
||||
|
||||
pub struct Canvas<D, S> {
|
||||
controls: Vec<control::Control<D, S>>,
|
||||
canvas: CanvasData<D>,
|
||||
|
||||
hover_controls: [Option<usize>; 2],
|
||||
pressed_controls: [Option<usize>; 2],
|
||||
|
||||
interact_map: Vec<Option<u16>>,
|
||||
interact_stride: usize,
|
||||
interact_rows: usize,
|
||||
|
||||
view_final: Arc<ImageView>,
|
||||
|
||||
pass_fg: WlxPass<WlxPipelineLegacy>,
|
||||
pass_bg: WlxPass<WlxPipelineLegacy>,
|
||||
}
|
||||
|
||||
impl<D, S> Canvas<D, S> {
|
||||
fn new(
|
||||
width: usize,
|
||||
height: usize,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
format: Format,
|
||||
data: D,
|
||||
) -> anyhow::Result<Self> {
|
||||
let tex_fg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_bg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_final = graphics.render_texture(width as _, height as _, format)?;
|
||||
|
||||
let view_fg = ImageView::new_default(tex_fg.clone())?;
|
||||
let view_bg = ImageView::new_default(tex_bg.clone())?;
|
||||
let view_final = ImageView::new_default(tex_final.clone())?;
|
||||
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
anyhow::bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let pipeline_bg_color = graphics.create_pipeline(
|
||||
view_bg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_fg_glyph = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_glyph").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_bg_sprite = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite2").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_hl_sprite = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite2_hl").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let vertex_buffer =
|
||||
graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _)?;
|
||||
|
||||
let pipeline_final = graphics.create_pipeline_with_layouts(
|
||||
view_final.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)?;
|
||||
|
||||
let set_fg =
|
||||
pipeline_final.uniform_sampler(0, view_fg.clone(), graphics.texture_filtering)?;
|
||||
let set_bg =
|
||||
pipeline_final.uniform_sampler(0, view_bg.clone(), graphics.texture_filtering)?;
|
||||
let pass_fg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_fg],
|
||||
)?;
|
||||
let pass_bg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_bg],
|
||||
)?;
|
||||
|
||||
let stride = width / RES_DIVIDER;
|
||||
let rows = height / RES_DIVIDER;
|
||||
|
||||
Ok(Self {
|
||||
canvas: CanvasData {
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
graphics: graphics.clone(),
|
||||
pipeline_bg_color,
|
||||
pipeline_fg_glyph,
|
||||
pipeline_bg_sprite,
|
||||
pipeline_hl_sprite,
|
||||
pipeline_final,
|
||||
},
|
||||
controls: Vec::new(),
|
||||
hover_controls: [None, None],
|
||||
pressed_controls: [None, None],
|
||||
interact_map: vec![None; stride * rows],
|
||||
interact_stride: stride,
|
||||
interact_rows: rows,
|
||||
view_final,
|
||||
pass_fg,
|
||||
pass_bg,
|
||||
})
|
||||
}
|
||||
|
||||
fn interactive_set_idx(&mut self, x: f32, y: f32, w: f32, h: f32, idx: usize) {
|
||||
let (x, y, w, h) = (x as usize, y as usize, w as usize, h as usize);
|
||||
|
||||
let x_min = (x / RES_DIVIDER).max(0);
|
||||
let y_min = (y / RES_DIVIDER).max(0);
|
||||
let x_max = (x_min + (w / RES_DIVIDER)).min(self.interact_stride - 1);
|
||||
let y_max = (y_min + (h / RES_DIVIDER)).min(self.interact_rows - 1);
|
||||
|
||||
for y in y_min..y_max {
|
||||
for x in x_min..x_max {
|
||||
self.interact_map[y * self.interact_stride + x] = Some(idx as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn interactive_get_idx(&self, uv: Vec2) -> Option<usize> {
|
||||
let x = (uv.x * self.canvas.width as f32) as usize;
|
||||
let y = (uv.y * self.canvas.height as f32) as usize;
|
||||
let x = (x / RES_DIVIDER).max(0).min(self.interact_stride - 1);
|
||||
let y = (y / RES_DIVIDER).max(0).min(self.interact_rows - 1);
|
||||
self.interact_map[y * self.interact_stride + x].map(|x| x as usize)
|
||||
}
|
||||
|
||||
fn render_bg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_bg_color)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_bg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
|
||||
fn render_fg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_fg_glyph)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_fg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
|
||||
self.hover_controls[pointer] = None;
|
||||
}
|
||||
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||
let old = self.hover_controls[hit.pointer];
|
||||
if let Some(i) = self.interactive_get_idx(hit.uv) {
|
||||
self.hover_controls[hit.pointer] = Some(i);
|
||||
} else {
|
||||
self.hover_controls[hit.pointer] = None;
|
||||
}
|
||||
if old != self.hover_controls[hit.pointer] {
|
||||
Some(Haptics {
|
||||
intensity: 0.1,
|
||||
duration: 0.01,
|
||||
frequency: 5.0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
let idx = if pressed {
|
||||
self.interactive_get_idx(hit.uv)
|
||||
} else {
|
||||
self.pressed_controls[hit.pointer]
|
||||
};
|
||||
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if pressed {
|
||||
if let Some(ref mut f) = c.on_press {
|
||||
self.pressed_controls[hit.pointer] = Some(idx);
|
||||
f(c, &mut self.canvas.data, app, hit.mode);
|
||||
}
|
||||
} else if let Some(ref mut f) = c.on_release {
|
||||
self.pressed_controls[hit.pointer] = None;
|
||||
f(c, &mut self.canvas.data, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
let idx = self.hover_controls[hit.pointer];
|
||||
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if let Some(ref mut f) = c.on_scroll {
|
||||
f(c, &mut self.canvas.data, app, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> OverlayRenderer for Canvas<D, S> {
|
||||
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)
|
||||
}
|
||||
fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut dirty = false;
|
||||
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_update {
|
||||
fun(c, &mut self.canvas.data, app);
|
||||
}
|
||||
if c.dirty {
|
||||
dirty = true;
|
||||
c.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if dirty {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)?;
|
||||
}
|
||||
|
||||
/*
|
||||
let image = self.view_final.image().clone();
|
||||
if self.first_render {
|
||||
self.first_render = false;
|
||||
} else {
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image.clone(),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
}
|
||||
*/
|
||||
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_final)?;
|
||||
|
||||
// static background
|
||||
cmd_buffer.run_ref(&self.pass_bg)?;
|
||||
|
||||
for (i, c) in self.controls.iter_mut().enumerate() {
|
||||
if let Some(render) = c.on_render_hl {
|
||||
if let Some(test) = c.test_highlight {
|
||||
if let Some(hl_color) = test(c, &mut self.canvas.data, app) {
|
||||
render(c, &self.canvas, app, &mut cmd_buffer, hl_color)?;
|
||||
}
|
||||
}
|
||||
if self.hover_controls.contains(&Some(i)) {
|
||||
render(
|
||||
c,
|
||||
&self.canvas,
|
||||
app,
|
||||
&mut cmd_buffer,
|
||||
Vec4::new(1., 1., 1., 0.3),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mostly static text
|
||||
cmd_buffer.run_ref(&self.pass_fg)?;
|
||||
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
|
||||
/*
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
*/
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
Some(self.view_final.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> OverlayBackend for Canvas<D, S> {
|
||||
fn set_renderer(&mut self, _renderer: Box<dyn OverlayRenderer>) {}
|
||||
fn set_interaction(&mut self, _interaction: Box<dyn InteractionHandler>) {}
|
||||
}
|
||||
@@ -222,7 +222,7 @@ impl FontCache {
|
||||
};
|
||||
|
||||
let mut cmd_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
let texture = cmd_buffer.texture2d(bmp.width() as _, bmp.rows() as _, format, &buf)?;
|
||||
let texture = cmd_buffer.texture2d_raw(bmp.width() as _, bmp.rows() as _, format, &buf)?;
|
||||
cmd_buffer.build_and_execute_now()?;
|
||||
|
||||
let g = Glyph {
|
||||
|
||||
800
src/gui/mod.rs
800
src/gui/mod.rs
@@ -1,37 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::bail;
|
||||
use glam::{Vec2, Vec4};
|
||||
use vulkano::{
|
||||
command_buffer::CommandBufferUsage,
|
||||
format::Format,
|
||||
image::{view::ImageView, ImageLayout},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
input::{Haptics, InteractionHandler, PointerHit, PointerMode},
|
||||
overlay::{OverlayBackend, OverlayRenderer},
|
||||
},
|
||||
graphics::{
|
||||
WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy, BLEND_ALPHA,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use self::modular::GuiColor;
|
||||
use glam::Vec4;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub mod canvas;
|
||||
pub mod font;
|
||||
pub mod modular;
|
||||
|
||||
const RES_DIVIDER: usize = 4;
|
||||
|
||||
struct Rect {
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
}
|
||||
pub type GuiColor = Vec4;
|
||||
pub(super) static FALLBACK_COLOR: Lazy<GuiColor> = Lazy::new(|| Vec4::new(1., 0., 1., 1.));
|
||||
|
||||
// Parses a color from a HTML hex string
|
||||
pub fn color_parse(html_hex: &str) -> anyhow::Result<GuiColor> {
|
||||
@@ -63,775 +38,12 @@ pub fn color_parse(html_hex: &str) -> anyhow::Result<GuiColor> {
|
||||
));
|
||||
}
|
||||
}
|
||||
bail!(
|
||||
anyhow::bail!(
|
||||
"Invalid color string: '{}', must be 7 characters long (e.g. #FF00FF)",
|
||||
&html_hex
|
||||
)
|
||||
}
|
||||
|
||||
pub struct CanvasBuilder<D, S> {
|
||||
canvas: Canvas<D, S>,
|
||||
|
||||
pub fg_color: GuiColor,
|
||||
pub bg_color: GuiColor,
|
||||
pub font_size: isize,
|
||||
}
|
||||
|
||||
impl<D, S> CanvasBuilder<D, S> {
|
||||
pub fn new(
|
||||
width: usize,
|
||||
height: usize,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
format: Format,
|
||||
data: D,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
canvas: Canvas::new(width, height, graphics, format, data)?,
|
||||
bg_color: Vec4::ZERO,
|
||||
fg_color: Vec4::ONE,
|
||||
font_size: 16,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build(self) -> Canvas<D, S> {
|
||||
self.canvas
|
||||
}
|
||||
|
||||
// Creates a panel with bg_color inherited from the canvas
|
||||
pub fn panel(&mut self, x: f32, y: f32, w: f32, h: f32) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
bg_color: self.bg_color,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a label with fg_color, font_size inherited from the canvas
|
||||
pub fn label(&mut self, x: f32, y: f32, w: f32, h: f32, text: Arc<str>) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(Control::render_text),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a label with fg_color, font_size inherited from the canvas
|
||||
#[allow(dead_code)]
|
||||
pub fn label_centered(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
text: Arc<str>,
|
||||
) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(Control::render_text_centered),
|
||||
..Control::new()
|
||||
});
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
// Creates a button with fg_color, bg_color, font_size inherited from the canvas
|
||||
pub fn button(&mut self, x: f32, y: f32, w: f32, h: f32, text: Arc<str>) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
|
||||
self.canvas.interactive_set_idx(x, y, w, h, idx);
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
text,
|
||||
fg_color: self.fg_color,
|
||||
bg_color: self.bg_color,
|
||||
size: self.font_size,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
on_render_fg: Some(Control::render_text_centered),
|
||||
on_render_hl: Some(Control::render_highlight),
|
||||
..Control::new()
|
||||
});
|
||||
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
|
||||
pub fn key_button(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
w: f32,
|
||||
h: f32,
|
||||
cap_type: KeyCapType,
|
||||
label: &[String],
|
||||
) -> &mut Control<D, S> {
|
||||
let idx = self.canvas.controls.len();
|
||||
self.canvas.interactive_set_idx(x, y, w, h, idx);
|
||||
|
||||
self.canvas.controls.push(Control {
|
||||
rect: Rect { x, y, w, h },
|
||||
bg_color: self.bg_color,
|
||||
on_render_bg: Some(Control::render_rect),
|
||||
on_render_hl: Some(Control::render_highlight),
|
||||
..Control::new()
|
||||
});
|
||||
|
||||
let renders = match cap_type {
|
||||
KeyCapType::Regular => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||
let rect = Rect {
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h: h - self.font_size as f32,
|
||||
};
|
||||
vec![(render, rect, 1f32)]
|
||||
}
|
||||
KeyCapType::RegularAltGr => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text;
|
||||
let rect0 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + (self.font_size as f32) + 12.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x: x + w * 0.5 + 12.,
|
||||
y: y + h - (self.font_size as f32) + 8.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
vec![(render, rect0, 1.0), (render, rect1, 0.8)]
|
||||
}
|
||||
KeyCapType::Reversed => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||
let rect0 = Rect {
|
||||
x,
|
||||
y: y + 2.0,
|
||||
w,
|
||||
h: h * 0.5,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x,
|
||||
y: y + h * 0.5 + 2.0,
|
||||
w,
|
||||
h: h * 0.5,
|
||||
};
|
||||
vec![(render, rect1, 1.0), (render, rect0, 0.8)]
|
||||
}
|
||||
KeyCapType::ReversedAltGr => {
|
||||
let render: ControlRenderer<D, S> = Control::render_text;
|
||||
let rect0 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + (self.font_size as f32) + 8.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect1 = Rect {
|
||||
x: x + 12.,
|
||||
y: y + h - (self.font_size as f32) + 4.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
let rect2 = Rect {
|
||||
x: x + w * 0.5 + 8.,
|
||||
y: y + h - (self.font_size as f32) + 4.,
|
||||
w,
|
||||
h,
|
||||
};
|
||||
vec![
|
||||
(render, rect1, 1.0),
|
||||
(render, rect0, 0.8),
|
||||
(render, rect2, 0.8),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
for (idx, (render, rect, alpha)) in renders.into_iter().enumerate() {
|
||||
if idx >= label.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
self.canvas.controls.push(Control {
|
||||
rect,
|
||||
text: Arc::from(label[idx].as_str()),
|
||||
fg_color: self.fg_color * alpha,
|
||||
size: self.font_size,
|
||||
on_render_fg: Some(render),
|
||||
..Control::new()
|
||||
});
|
||||
}
|
||||
|
||||
&mut self.canvas.controls[idx]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanvasData<D> {
|
||||
pub data: D,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
|
||||
graphics: Arc<WlxGraphics>,
|
||||
|
||||
pipeline_bg_color: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_fg_glyph: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
pipeline_final: Arc<WlxPipeline<WlxPipelineLegacy>>,
|
||||
}
|
||||
|
||||
pub struct Canvas<D, S> {
|
||||
controls: Vec<Control<D, S>>,
|
||||
canvas: CanvasData<D>,
|
||||
|
||||
hover_controls: [Option<usize>; 2],
|
||||
pressed_controls: [Option<usize>; 2],
|
||||
|
||||
interact_map: Vec<Option<u16>>,
|
||||
interact_stride: usize,
|
||||
interact_rows: usize,
|
||||
|
||||
view_final: Arc<ImageView>,
|
||||
|
||||
pass_fg: WlxPass<WlxPipelineLegacy>,
|
||||
pass_bg: WlxPass<WlxPipelineLegacy>,
|
||||
}
|
||||
|
||||
impl<D, S> Canvas<D, S> {
|
||||
fn new(
|
||||
width: usize,
|
||||
height: usize,
|
||||
graphics: Arc<WlxGraphics>,
|
||||
format: Format,
|
||||
data: D,
|
||||
) -> anyhow::Result<Self> {
|
||||
let tex_fg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_bg = graphics.render_texture(width as _, height as _, format)?;
|
||||
let tex_final = graphics.render_texture(width as _, height as _, format)?;
|
||||
|
||||
let view_fg = ImageView::new_default(tex_fg.clone())?;
|
||||
let view_bg = ImageView::new_default(tex_bg.clone())?;
|
||||
let view_final = ImageView::new_default(tex_final.clone())?;
|
||||
|
||||
let Ok(shaders) = graphics.shared_shaders.read() else {
|
||||
bail!("Failed to lock shared shaders for reading");
|
||||
};
|
||||
|
||||
let pipeline_bg_color = graphics.create_pipeline(
|
||||
view_bg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_color").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let pipeline_fg_glyph = graphics.create_pipeline(
|
||||
view_fg.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_glyph").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
)?;
|
||||
|
||||
let vertex_buffer =
|
||||
graphics.upload_verts(width as _, height as _, 0., 0., width as _, height as _)?;
|
||||
|
||||
let pipeline_final = graphics.create_pipeline_with_layouts(
|
||||
view_final.clone(),
|
||||
shaders.get("vert_common").unwrap().clone(), // want panic
|
||||
shaders.get("frag_sprite").unwrap().clone(), // want panic
|
||||
format,
|
||||
Some(BLEND_ALPHA),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)?;
|
||||
|
||||
let set_fg =
|
||||
pipeline_final.uniform_sampler(0, view_fg.clone(), graphics.texture_filtering)?;
|
||||
let set_bg =
|
||||
pipeline_final.uniform_sampler(0, view_bg.clone(), graphics.texture_filtering)?;
|
||||
let pass_fg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_fg],
|
||||
)?;
|
||||
let pass_bg = pipeline_final.create_pass(
|
||||
[width as _, height as _],
|
||||
vertex_buffer.clone(),
|
||||
graphics.quad_indices.clone(),
|
||||
vec![set_bg],
|
||||
)?;
|
||||
|
||||
let stride = width / RES_DIVIDER;
|
||||
let rows = height / RES_DIVIDER;
|
||||
|
||||
Ok(Self {
|
||||
canvas: CanvasData {
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
graphics: graphics.clone(),
|
||||
pipeline_bg_color,
|
||||
pipeline_fg_glyph,
|
||||
pipeline_final,
|
||||
},
|
||||
controls: Vec::new(),
|
||||
hover_controls: [None, None],
|
||||
pressed_controls: [None, None],
|
||||
interact_map: vec![None; stride * rows],
|
||||
interact_stride: stride,
|
||||
interact_rows: rows,
|
||||
view_final,
|
||||
pass_fg,
|
||||
pass_bg,
|
||||
})
|
||||
}
|
||||
|
||||
fn interactive_set_idx(&mut self, x: f32, y: f32, w: f32, h: f32, idx: usize) {
|
||||
let (x, y, w, h) = (x as usize, y as usize, w as usize, h as usize);
|
||||
|
||||
let x_min = (x / RES_DIVIDER).max(0);
|
||||
let y_min = (y / RES_DIVIDER).max(0);
|
||||
let x_max = (x_min + (w / RES_DIVIDER)).min(self.interact_stride - 1);
|
||||
let y_max = (y_min + (h / RES_DIVIDER)).min(self.interact_rows - 1);
|
||||
|
||||
for y in y_min..y_max {
|
||||
for x in x_min..x_max {
|
||||
self.interact_map[y * self.interact_stride + x] = Some(idx as u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn interactive_get_idx(&self, uv: Vec2) -> Option<usize> {
|
||||
let x = (uv.x * self.canvas.width as f32) as usize;
|
||||
let y = (uv.y * self.canvas.height as f32) as usize;
|
||||
let x = (x / RES_DIVIDER).max(0).min(self.interact_stride - 1);
|
||||
let y = (y / RES_DIVIDER).max(0).min(self.interact_rows - 1);
|
||||
self.interact_map[y * self.interact_stride + x].map(|x| x as usize)
|
||||
}
|
||||
|
||||
fn render_bg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_bg_color)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_bg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
|
||||
fn render_fg(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_fg_glyph)?;
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_render_fg {
|
||||
fun(c, &self.canvas, app, &mut cmd_buffer)?;
|
||||
}
|
||||
}
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> InteractionHandler for Canvas<D, S> {
|
||||
fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
|
||||
self.hover_controls[pointer] = None;
|
||||
}
|
||||
fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
|
||||
let old = self.hover_controls[hit.pointer];
|
||||
if let Some(i) = self.interactive_get_idx(hit.uv) {
|
||||
self.hover_controls[hit.pointer] = Some(i);
|
||||
} else {
|
||||
self.hover_controls[hit.pointer] = None;
|
||||
}
|
||||
if old != self.hover_controls[hit.pointer] {
|
||||
Some(Haptics {
|
||||
intensity: 0.1,
|
||||
duration: 0.01,
|
||||
frequency: 5.0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
|
||||
let idx = if pressed {
|
||||
self.interactive_get_idx(hit.uv)
|
||||
} else {
|
||||
self.pressed_controls[hit.pointer]
|
||||
};
|
||||
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if pressed {
|
||||
if let Some(ref mut f) = c.on_press {
|
||||
self.pressed_controls[hit.pointer] = Some(idx);
|
||||
f(c, &mut self.canvas.data, app, hit.mode);
|
||||
}
|
||||
} else if let Some(ref mut f) = c.on_release {
|
||||
self.pressed_controls[hit.pointer] = None;
|
||||
f(c, &mut self.canvas.data, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
|
||||
let idx = self.hover_controls[hit.pointer];
|
||||
|
||||
if let Some(idx) = idx {
|
||||
let c = &mut self.controls[idx];
|
||||
if let Some(ref mut f) = c.on_scroll {
|
||||
f(c, &mut self.canvas.data, app, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> OverlayRenderer for Canvas<D, S> {
|
||||
fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)
|
||||
}
|
||||
fn pause(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn resume(&mut self, _app: &mut AppState) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render(&mut self, app: &mut AppState) -> anyhow::Result<()> {
|
||||
let mut dirty = false;
|
||||
|
||||
for c in self.controls.iter_mut() {
|
||||
if let Some(fun) = c.on_update {
|
||||
fun(c, &mut self.canvas.data, app);
|
||||
}
|
||||
if c.dirty {
|
||||
dirty = true;
|
||||
c.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if dirty {
|
||||
self.render_bg(app)?;
|
||||
self.render_fg(app)?;
|
||||
}
|
||||
|
||||
/*
|
||||
let image = self.view_final.image().clone();
|
||||
if self.first_render {
|
||||
self.first_render = false;
|
||||
} else {
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image.clone(),
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
}
|
||||
*/
|
||||
|
||||
let mut cmd_buffer = self
|
||||
.canvas
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_render_pass(&self.canvas.pipeline_final)?;
|
||||
|
||||
// static background
|
||||
cmd_buffer.run_ref(&self.pass_bg)?;
|
||||
|
||||
for (i, c) in self.controls.iter_mut().enumerate() {
|
||||
if let Some(render) = c.on_render_hl {
|
||||
if let Some(test) = c.test_highlight {
|
||||
if let Some(hl_color) = test(c, &mut self.canvas.data, app) {
|
||||
render(c, &self.canvas, app, &mut cmd_buffer, hl_color)?;
|
||||
}
|
||||
}
|
||||
if self.hover_controls.contains(&Some(i)) {
|
||||
render(
|
||||
c,
|
||||
&self.canvas,
|
||||
app,
|
||||
&mut cmd_buffer,
|
||||
Vec4::new(1., 1., 1., 0.3),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mostly static text
|
||||
cmd_buffer.run_ref(&self.pass_fg)?;
|
||||
|
||||
cmd_buffer.end_render_pass()?;
|
||||
cmd_buffer.build_and_execute_now()
|
||||
|
||||
/*
|
||||
self.canvas
|
||||
.graphics
|
||||
.transition_layout(
|
||||
image,
|
||||
ImageLayout::ColorAttachmentOptimal,
|
||||
ImageLayout::TransferSrcOptimal,
|
||||
)
|
||||
.wait(None)
|
||||
.unwrap();
|
||||
*/
|
||||
}
|
||||
fn view(&mut self) -> Option<Arc<ImageView>> {
|
||||
Some(self.view_final.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, S> OverlayBackend for Canvas<D, S> {
|
||||
fn set_renderer(&mut self, _renderer: Box<dyn OverlayRenderer>) {}
|
||||
fn set_interaction(&mut self, _interaction: Box<dyn InteractionHandler>) {}
|
||||
}
|
||||
|
||||
pub type ControlRenderer<D, S> =
|
||||
fn(&Control<D, S>, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>;
|
||||
|
||||
pub type ControlRendererHl<D, S> = fn(
|
||||
&Control<D, S>,
|
||||
&CanvasData<D>,
|
||||
&mut AppState,
|
||||
&mut WlxCommandBuffer,
|
||||
Vec4,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
pub struct Control<D, S> {
|
||||
pub state: Option<S>,
|
||||
rect: Rect,
|
||||
fg_color: GuiColor,
|
||||
bg_color: GuiColor,
|
||||
text: Arc<str>,
|
||||
size: isize,
|
||||
dirty: bool,
|
||||
|
||||
pub on_update: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_press: Option<fn(&mut Self, &mut D, &mut AppState, PointerMode)>,
|
||||
pub on_release: Option<fn(&mut Self, &mut D, &mut AppState)>,
|
||||
pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32)>,
|
||||
pub test_highlight: Option<fn(&Self, &mut D, &mut AppState) -> Option<Vec4>>,
|
||||
|
||||
on_render_bg: Option<ControlRenderer<D, S>>,
|
||||
on_render_hl: Option<ControlRendererHl<D, S>>,
|
||||
on_render_fg: Option<ControlRenderer<D, S>>,
|
||||
}
|
||||
|
||||
impl<D, S> Control<D, S> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
rect: Rect {
|
||||
x: 0.,
|
||||
y: 0.,
|
||||
w: 0.,
|
||||
h: 0.,
|
||||
},
|
||||
fg_color: Vec4::ONE,
|
||||
bg_color: Vec4::ZERO,
|
||||
text: Arc::from(""),
|
||||
dirty: true,
|
||||
size: 24,
|
||||
state: None,
|
||||
on_update: None,
|
||||
on_render_bg: None,
|
||||
on_render_hl: None,
|
||||
on_render_fg: None,
|
||||
test_highlight: None,
|
||||
on_press: None,
|
||||
on_release: None,
|
||||
on_scroll: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
if *self.text == *text {
|
||||
return;
|
||||
}
|
||||
self.text = text.into();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_fg_color(&mut self, color: GuiColor) {
|
||||
if self.fg_color == color {
|
||||
return;
|
||||
}
|
||||
self.fg_color = color;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
fn render_rect(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
_: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let pass = {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
let set0 = canvas
|
||||
.pipeline_bg_color
|
||||
.uniform_buffer(0, self.bg_color.to_array().to_vec())?;
|
||||
canvas.pipeline_bg_color.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)?
|
||||
};
|
||||
|
||||
cmd_buffer.run_ref(&pass)
|
||||
}
|
||||
|
||||
fn render_highlight(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
_: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
color: GuiColor,
|
||||
) -> anyhow::Result<()> {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
)?;
|
||||
|
||||
let set0 = canvas
|
||||
.pipeline_bg_color
|
||||
.uniform_buffer(0, color.to_array().to_vec())?;
|
||||
|
||||
let pass = canvas.pipeline_bg_color.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer.clone(),
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0],
|
||||
)?;
|
||||
|
||||
cmd_buffer.run_ref(&pass)
|
||||
}
|
||||
|
||||
fn render_text(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut cur_y = self.rect.y;
|
||||
for line in self.text.lines() {
|
||||
let mut cur_x = self.rect.x;
|
||||
for glyph in app
|
||||
.fc
|
||||
.get_glyphs(line, self.size, canvas.graphics.clone())?
|
||||
{
|
||||
if let Some(tex) = glyph.tex.clone() {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
cur_x + glyph.left,
|
||||
cur_y - glyph.top,
|
||||
glyph.width,
|
||||
glyph.height,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_fg_glyph.uniform_sampler(
|
||||
0,
|
||||
ImageView::new_default(tex)?,
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set1 = canvas
|
||||
.pipeline_fg_glyph
|
||||
.uniform_buffer(1, self.fg_color.to_array().to_vec())?;
|
||||
let pass = canvas.pipeline_fg_glyph.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
}
|
||||
cur_x += glyph.advance;
|
||||
}
|
||||
cur_y += (self.size as f32) * 1.5;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn render_text_centered(
|
||||
&self,
|
||||
canvas: &CanvasData<D>,
|
||||
app: &mut AppState,
|
||||
cmd_buffer: &mut WlxCommandBuffer,
|
||||
) -> anyhow::Result<()> {
|
||||
let (w, h) = app
|
||||
.fc
|
||||
.get_text_size(&self.text, self.size, canvas.graphics.clone())?;
|
||||
|
||||
let mut cur_y = self.rect.y + (self.rect.h) - (h * 0.5) - (self.size as f32 * 0.25);
|
||||
for line in self.text.lines() {
|
||||
let mut cur_x = self.rect.x + (self.rect.w * 0.5) - (w * 0.5);
|
||||
for glyph in app
|
||||
.fc
|
||||
.get_glyphs(line, self.size, canvas.graphics.clone())?
|
||||
{
|
||||
if let Some(tex) = glyph.tex.clone() {
|
||||
let vertex_buffer = canvas.graphics.upload_verts(
|
||||
canvas.width as _,
|
||||
canvas.height as _,
|
||||
cur_x + glyph.left,
|
||||
cur_y - glyph.top,
|
||||
glyph.width,
|
||||
glyph.height,
|
||||
)?;
|
||||
let set0 = canvas.pipeline_fg_glyph.uniform_sampler(
|
||||
0,
|
||||
ImageView::new_default(tex)?,
|
||||
app.graphics.texture_filtering,
|
||||
)?;
|
||||
let set1 = canvas
|
||||
.pipeline_fg_glyph
|
||||
.uniform_buffer(1, self.fg_color.to_array().to_vec())?;
|
||||
let pass = canvas.pipeline_fg_glyph.create_pass(
|
||||
[canvas.width as _, canvas.height as _],
|
||||
vertex_buffer,
|
||||
canvas.graphics.quad_indices.clone(),
|
||||
vec![set0, set1],
|
||||
)?;
|
||||
cmd_buffer.run_ref(&pass)?;
|
||||
}
|
||||
cur_x += glyph.advance;
|
||||
}
|
||||
cur_y += (self.size as f32) * 1.5;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum KeyCapType {
|
||||
/// Label is in center of keycap
|
||||
Regular,
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
pub mod button;
|
||||
pub mod label;
|
||||
//pub mod slider;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{fs::File, sync::Arc};
|
||||
|
||||
use glam::Vec4;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
|
||||
|
||||
use crate::{backend::common::OverlaySelector, state::AppState};
|
||||
use crate::{
|
||||
backend::common::OverlaySelector, config::AStrMapExt, config_io::CONFIG_ROOT_PATH,
|
||||
graphics::dds::WlxCommandBufferDds, state::AppState,
|
||||
};
|
||||
|
||||
use self::{
|
||||
button::{modular_button_init, ButtonAction, ButtonData, OverlayAction},
|
||||
label::{modular_label_init, LabelContent, LabelData},
|
||||
};
|
||||
|
||||
use super::{color_parse, Canvas, CanvasBuilder, Control};
|
||||
use super::{
|
||||
canvas::{builder::CanvasBuilder, control::Control, Canvas},
|
||||
color_parse, GuiColor, FALLBACK_COLOR,
|
||||
};
|
||||
|
||||
type ModularControl = Control<(), ModularData>;
|
||||
type ExecArgs = Vec<Arc<str>>;
|
||||
|
||||
pub type GuiColor = Vec4;
|
||||
|
||||
static FALLBACK_COLOR: Lazy<GuiColor> = Lazy::new(|| Vec4::new(1., 0., 1., 1.));
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ModularUiConfig {
|
||||
pub width: f32,
|
||||
@@ -69,6 +70,11 @@ pub enum ModularElement {
|
||||
#[serde(flatten)]
|
||||
data: LabelContent,
|
||||
},
|
||||
Sprite {
|
||||
rect: [f32; 4],
|
||||
sprite: Arc<str>,
|
||||
sprite_st: Option<[f32; 4]>,
|
||||
},
|
||||
Button {
|
||||
rect: [f32; 4],
|
||||
font_size: isize,
|
||||
@@ -120,7 +126,7 @@ pub enum ModularData {
|
||||
pub fn modular_canvas(
|
||||
size: &[u32; 2],
|
||||
elements: &[ModularElement],
|
||||
state: &AppState,
|
||||
state: &mut AppState,
|
||||
) -> anyhow::Result<Canvas<(), ModularData>> {
|
||||
let mut canvas = CanvasBuilder::new(
|
||||
size[0] as _,
|
||||
@@ -161,6 +167,25 @@ pub fn modular_canvas(
|
||||
let label = canvas.label_centered(*x, *y, *w, *h, empty_str.clone());
|
||||
modular_label_init(label, data);
|
||||
}
|
||||
ModularElement::Sprite {
|
||||
rect: [x, y, w, h],
|
||||
sprite,
|
||||
sprite_st,
|
||||
} => match sprite_from_path(sprite.clone(), state) {
|
||||
Ok(view) => {
|
||||
let sprite = canvas.sprite(*x, *y, *w, *h);
|
||||
sprite.fg_color = Vec4::ONE;
|
||||
sprite.set_sprite(view);
|
||||
|
||||
let st = sprite_st
|
||||
.map(|st| Vec4::from_slice(&st))
|
||||
.unwrap_or_else(|| Vec4::new(1., 1., 0., 0.));
|
||||
sprite.set_sprite_st(st);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Could not load custom UI sprite: {:?}", e);
|
||||
}
|
||||
},
|
||||
ModularElement::Button {
|
||||
rect: [x, y, w, h],
|
||||
font_size,
|
||||
@@ -349,3 +374,29 @@ pub fn color_parse_or_default(color: &str) -> GuiColor {
|
||||
*FALLBACK_COLOR
|
||||
})
|
||||
}
|
||||
|
||||
fn sprite_from_path(path: Arc<str>, app: &mut AppState) -> anyhow::Result<Arc<ImageView>> {
|
||||
if let Some(view) = app.sprites.arc_get(&path) {
|
||||
return Ok(view.clone());
|
||||
}
|
||||
|
||||
let real_path = CONFIG_ROOT_PATH.join(&*path);
|
||||
|
||||
let Ok(f) = File::open(real_path) else {
|
||||
anyhow::bail!("Could not open custom sprite at: {}", path);
|
||||
};
|
||||
|
||||
let mut command_buffer = app
|
||||
.graphics
|
||||
.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
|
||||
match command_buffer.texture2d_dds(f) {
|
||||
Ok(image) => {
|
||||
command_buffer.build_and_execute_now()?;
|
||||
Ok(ImageView::new_default(image)?)
|
||||
}
|
||||
Err(e) => {
|
||||
anyhow::bail!("Could not use custom sprite at: {}\n{:?}", path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/image.png
BIN
src/image.png
Binary file not shown.
|
Before Width: | Height: | Size: 145 KiB |
@@ -9,7 +9,7 @@ use crate::state::AppState;
|
||||
|
||||
pub static ANCHOR_NAME: Lazy<Arc<str>> = Lazy::new(|| Arc::from("anchor"));
|
||||
|
||||
pub fn create_anchor<O>(state: &AppState) -> anyhow::Result<OverlayData<O>>
|
||||
pub fn create_anchor<O>(state: &mut AppState) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
const SETTINGS_NAME: &str = "settings";
|
||||
|
||||
pub fn create_custom(
|
||||
state: &AppState,
|
||||
state: &mut AppState,
|
||||
name: Arc<str>,
|
||||
) -> Option<(OverlayState, Box<dyn OverlayBackend>)> {
|
||||
let config = if &*name == SETTINGS_NAME {
|
||||
|
||||
@@ -10,7 +10,10 @@ use crate::{
|
||||
overlay::{OverlayData, OverlayState},
|
||||
},
|
||||
config::{self, ConfigType},
|
||||
gui::{color_parse, CanvasBuilder, Control, KeyCapType},
|
||||
gui::{
|
||||
canvas::{builder::CanvasBuilder, control::Control},
|
||||
color_parse, KeyCapType,
|
||||
},
|
||||
hid::{
|
||||
get_key_type, KeyModifier, KeyType, VirtualKey, XkbKeymap, ALT, CTRL, KEYS_TO_MODS, META,
|
||||
NUM_LOCK, SHIFT, SUPER,
|
||||
|
||||
@@ -214,7 +214,7 @@ impl ScreenPipeline {
|
||||
];
|
||||
|
||||
let mouse_tex =
|
||||
uploads.texture2d(4, 4, vulkano::format::Format::R8G8B8A8_UNORM, &mouse_bytes)?;
|
||||
uploads.texture2d_raw(4, 4, vulkano::format::Format::R8G8B8A8_UNORM, &mouse_bytes)?;
|
||||
self.mouse = Some(ImageView::new_default(mouse_tex)?);
|
||||
Ok(())
|
||||
}
|
||||
@@ -507,8 +507,12 @@ impl OverlayRenderer for ScreenRenderer {
|
||||
|
||||
let data = unsafe { slice::from_raw_parts(map, len) };
|
||||
|
||||
let image =
|
||||
upload.texture2d(frame.format.width, frame.format.height, format, data)?;
|
||||
let image = upload.texture2d_raw(
|
||||
frame.format.width,
|
||||
frame.format.height,
|
||||
format,
|
||||
data,
|
||||
)?;
|
||||
upload.build_and_execute_now()?;
|
||||
|
||||
unsafe { libc::munmap(map as *mut _, len) };
|
||||
@@ -526,8 +530,12 @@ impl OverlayRenderer for ScreenRenderer {
|
||||
|
||||
let data = unsafe { slice::from_raw_parts(frame.ptr as *const u8, frame.size) };
|
||||
|
||||
let image =
|
||||
upload.texture2d(frame.format.width, frame.format.height, format, data)?;
|
||||
let image = upload.texture2d_raw(
|
||||
frame.format.width,
|
||||
frame.format.height,
|
||||
format,
|
||||
data,
|
||||
)?;
|
||||
|
||||
let pipeline = Some(match self.pipeline {
|
||||
Some(ref mut p) => p,
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
overlay::{OverlayBackend, OverlayState, RelativeTo},
|
||||
task::TaskType,
|
||||
},
|
||||
gui::{color_parse, CanvasBuilder},
|
||||
gui::{canvas::builder::CanvasBuilder, color_parse},
|
||||
state::{AppState, LeftRight},
|
||||
};
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ use crate::{
|
||||
backend::overlay::{ui_transform, OverlayData, OverlayState, RelativeTo},
|
||||
config::{load_known_yaml, ConfigType},
|
||||
gui::{
|
||||
canvas::Canvas,
|
||||
modular::{modular_canvas, ModularData, ModularUiConfig},
|
||||
Canvas,
|
||||
},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
pub const WATCH_NAME: &str = "watch";
|
||||
|
||||
pub fn create_watch<O>(state: &AppState) -> anyhow::Result<OverlayData<O>>
|
||||
pub fn create_watch<O>(state: &mut AppState) -> anyhow::Result<OverlayData<O>>
|
||||
where
|
||||
O: Default,
|
||||
{
|
||||
@@ -40,7 +40,7 @@ where
|
||||
|
||||
pub fn create_watch_canvas(
|
||||
config: Option<ModularUiConfig>,
|
||||
state: &AppState,
|
||||
state: &mut AppState,
|
||||
) -> anyhow::Result<Canvas<(), ModularData>> {
|
||||
let config = config.unwrap_or_else(|| load_known_yaml::<ModularUiConfig>(ConfigType::Watch));
|
||||
|
||||
|
||||
BIN
src/res/table_mountain_2.dds
Normal file
BIN
src/res/table_mountain_2.dds
Normal file
Binary file not shown.
@@ -61,6 +61,52 @@ pub mod frag_glyph {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_sprite2 {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: r"#version 310 es
|
||||
precision highp float;
|
||||
|
||||
layout (location = 0) in vec2 in_uv;
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform sampler2D in_texture;
|
||||
layout (set = 1, binding = 0) uniform UniBlock {
|
||||
uniform vec4 st;
|
||||
uniform vec4 mul;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = texture(in_texture, (in_uv * st.xy) + st.zw) * mul;
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_sprite2_hl {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: r"#version 310 es
|
||||
precision highp float;
|
||||
|
||||
layout (location = 0) in vec2 in_uv;
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform sampler2D in_texture;
|
||||
layout (set = 1, binding = 0) uniform UniBlock {
|
||||
uniform vec4 st;
|
||||
uniform vec4 mul;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = texture(in_texture, (in_uv * st.xy) + st.zw).a * mul;
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_sprite {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
@@ -80,6 +126,31 @@ pub mod frag_sprite {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_grid {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: r"#version 310 es
|
||||
precision highp float;
|
||||
|
||||
layout (location = 0) in vec2 in_uv;
|
||||
layout (location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
float fade = max(1.0 - 2.0 * length(in_uv.xy + vec2(-0.5, -0.5)), 0.0);
|
||||
float grid;
|
||||
|
||||
if (fract(in_uv.x / 0.0005) < 0.01 || fract(in_uv.y / 0.0005) < 0.01) {
|
||||
grid = 1.0;
|
||||
} else {
|
||||
grid = 0.0;
|
||||
}
|
||||
out_color = vec4(1.0, 1.0, 1.0, grid * fade);
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
pub mod frag_screen {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
@@ -153,7 +224,7 @@ pub mod frag_swapchain {
|
||||
void main()
|
||||
{
|
||||
out_color = texture(in_texture, in_uv);
|
||||
out_color.a = alpha;
|
||||
out_color.a *= alpha;
|
||||
}
|
||||
",
|
||||
}
|
||||
|
||||
19
src/state.rs
19
src/state.rs
@@ -6,16 +6,20 @@ use idmap::IdMap;
|
||||
use rodio::{Decoder, OutputStream, OutputStreamHandle, Source};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use vulkano::image::view::ImageView;
|
||||
|
||||
use crate::{
|
||||
backend::{input::InputState, task::TaskContainer},
|
||||
config::GeneralConfig,
|
||||
config::{AStrMap, GeneralConfig},
|
||||
config_io,
|
||||
graphics::WlxGraphics,
|
||||
gui::font::FontCache,
|
||||
hid::HidProvider,
|
||||
overlays::toast::{DisplayMethod, ToastTopic},
|
||||
shaders::{frag_color, frag_glyph, frag_screen, frag_sprite, frag_swapchain, vert_common},
|
||||
shaders::{
|
||||
frag_color, frag_glyph, frag_grid, frag_screen, frag_sprite, frag_sprite2, frag_sprite2_hl,
|
||||
frag_swapchain, vert_common,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct AppState {
|
||||
@@ -28,6 +32,7 @@ pub struct AppState {
|
||||
pub audio: AudioOutput,
|
||||
pub screens: SmallVec<[ScreenMeta; 8]>,
|
||||
pub anchor: Affine3A,
|
||||
pub sprites: AStrMap<Arc<ImageView>>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -47,9 +52,18 @@ impl AppState {
|
||||
let shader = frag_glyph::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_glyph", shader);
|
||||
|
||||
let shader = frag_grid::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_grid", shader);
|
||||
|
||||
let shader = frag_sprite::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_sprite", shader);
|
||||
|
||||
let shader = frag_sprite2::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_sprite2", shader);
|
||||
|
||||
let shader = frag_sprite2_hl::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_sprite2_hl", shader);
|
||||
|
||||
let shader = frag_screen::load(graphics.device.clone())?;
|
||||
shaders.insert("frag_screen", shader);
|
||||
|
||||
@@ -69,6 +83,7 @@ impl AppState {
|
||||
audio: AudioOutput::new(),
|
||||
screens: smallvec![],
|
||||
anchor: Affine3A::IDENTITY,
|
||||
sprites: AStrMap::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user