added underdeveloped but working image support (no freeing or samplers)

This commit is contained in:
2025-08-22 23:07:31 -04:00
parent bde929b05a
commit 7dbdcbba42
26 changed files with 1256 additions and 155 deletions

702
Cargo.lock generated
View File

@@ -18,6 +18,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.12" version = "0.8.12"
@@ -25,12 +31,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom 0.3.3",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
] ]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]] [[package]]
name = "android-activity" name = "android-activity"
version = "0.6.0" version = "0.6.0"
@@ -67,6 +82,29 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anyhow"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.9" version = "0.3.9"
@@ -106,6 +144,29 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av1-grain"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
dependencies = [
"arrayvec",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.8.0" version = "0.8.0"
@@ -121,6 +182,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@@ -136,6 +203,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitstream-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]] [[package]]
name = "block" name = "block"
version = "0.1.6" version = "0.1.6"
@@ -151,6 +224,12 @@ dependencies = [
"objc2", "objc2",
] ]
[[package]]
name = "built"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.19.0" version = "3.19.0"
@@ -177,6 +256,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.10.1" version = "1.10.1"
@@ -226,6 +311,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.1" version = "1.0.1"
@@ -249,6 +344,12 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@@ -329,6 +430,34 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@@ -383,6 +512,32 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@@ -399,6 +554,40 @@ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
[[package]]
name = "exr"
version = "1.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
dependencies = [
"bit_field",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.1.5"
@@ -442,6 +631,17 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.3" version = "0.3.3"
@@ -451,7 +651,17 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasi", "wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "gif"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
dependencies = [
"color_quant",
"weezl",
] ]
[[package]] [[package]]
@@ -537,16 +747,6 @@ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
] ]
[[package]]
name = "gui"
version = "0.1.0"
dependencies = [
"bytemuck",
"pollster",
"wgpu",
"winit",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.6.0" version = "2.6.0"
@@ -567,6 +767,12 @@ dependencies = [
"foldhash", "foldhash",
] ]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.2" version = "0.5.2"
@@ -579,6 +785,45 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "image"
version = "0.25.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"exr",
"gif",
"image-webp",
"num-traits",
"png",
"qoi",
"ravif",
"rayon",
"rgb",
"tiff",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imgref"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.10.0" version = "2.10.0"
@@ -589,6 +834,26 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]] [[package]]
name = "jni" name = "jni"
version = "0.21.1" version = "0.21.1"
@@ -617,10 +882,16 @@ version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.3.3",
"libc", "libc",
] ]
[[package]]
name = "jpeg-decoder"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.77" version = "0.3.77"
@@ -648,12 +919,28 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libfuzzer-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
]
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.8.8" version = "0.8.8"
@@ -715,6 +1002,15 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]] [[package]]
name = "malloc_buf" name = "malloc_buf"
version = "0.0.6" version = "0.0.6"
@@ -724,6 +1020,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
"rayon",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.5" version = "2.7.5"
@@ -754,6 +1060,22 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]] [[package]]
name = "naga" name = "naga"
version = "26.0.0" version = "26.0.0"
@@ -810,6 +1132,69 @@ dependencies = [
"jni-sys", "jni-sys",
] ]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -1154,6 +1539,19 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]] [[package]]
name = "polling" name = "polling"
version = "3.10.0" version = "3.10.0"
@@ -1189,6 +1587,15 @@ dependencies = [
"portable-atomic", "portable-atomic",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "presser" name = "presser"
version = "0.3.1" version = "0.3.1"
@@ -1218,6 +1625,34 @@ name = "profiling"
version = "1.0.17" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
@@ -1243,18 +1678,118 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
]
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.4" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
[[package]]
name = "rav1e"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
dependencies = [
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"once_cell",
"paste",
"profiling",
"rand",
"rand_chacha",
"simd_helpers",
"system-deps",
"thiserror 1.0.69",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.11.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rayon",
"rgb",
]
[[package]] [[package]]
name = "raw-window-handle" name = "raw-window-handle"
version = "0.6.2" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rayon"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.4.1" version = "0.4.1"
@@ -1279,6 +1814,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@@ -1371,12 +1912,36 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.11" version = "0.4.11"
@@ -1464,6 +2029,25 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@@ -1513,6 +2097,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tiff"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]] [[package]]
name = "tiny-skia" name = "tiny-skia"
version = "0.11.4" version = "0.11.4"
@@ -1538,11 +2133,26 @@ dependencies = [
"strict-num", "strict-num",
] ]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.11" version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
@@ -1551,6 +2161,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde",
"serde_spanned",
"toml_datetime", "toml_datetime",
"winnow", "winnow",
] ]
@@ -1577,6 +2189,17 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "ui"
version = "0.1.0"
dependencies = [
"bytemuck",
"image",
"pollster",
"wgpu",
"winit",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
@@ -1595,6 +2218,23 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "version-compare"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@@ -1611,6 +2251,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.2+wasi-0.2.4" version = "0.14.2+wasi-0.2.4"
@@ -1820,6 +2466,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "weezl"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
[[package]] [[package]]
name = "wgpu" name = "wgpu"
version = "26.0.1" version = "26.0.1"
@@ -2479,3 +3131,27 @@ dependencies = [
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "zune-core"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zune-jpeg"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089"
dependencies = [
"zune-core",
]

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "gui" name = "ui"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
@@ -10,4 +10,5 @@ pollster = "0.4.0"
winit = "0.30.11" winit = "0.30.11"
wgpu = "26.0.1" wgpu = "26.0.1"
bytemuck = "1.23.1" bytemuck = "1.23.1"
image = "0.25.6"

View File

@@ -8,7 +8,7 @@ pub struct Regioned {
impl<Ctx: 'static> Widget<Ctx> for Regioned { impl<Ctx: 'static> Widget<Ctx> for Regioned {
fn draw(&self, painter: &mut Painter<Ctx>) { fn draw(&self, painter: &mut Painter<Ctx>) {
painter.region.select(&self.region); painter.region.select(&self.region);
painter.draw(&self.inner); painter.draw_inner(&self.inner);
} }
} }

63
src/core/image.rs Normal file
View File

@@ -0,0 +1,63 @@
use image::DynamicImage;
use crate::{Color, TextureHandle, Widget, WidgetFnRet, render::RectPrimitive};
pub struct Image {
handle: Option<TextureHandle>,
}
impl<Ctx> Widget<Ctx> for Image {
fn draw(&self, painter: &mut crate::Painter<Ctx>) {
if let Some(handle) = &self.handle {
painter.draw_texture(handle);
} else {
painter.write(RectPrimitive {
color: Color::MAGENTA,
inner_radius: 0.0,
radius: 0.0,
thickness: 0.0,
});
}
}
}
pub fn image<Ctx>(image: impl LoadableImage) -> WidgetFnRet!(Image, Ctx) {
let image = match image.get_image() {
Ok(image) => Some(image),
Err(e) => {
println!("Failed to load image: {e}");
None
}
};
move |ui| Image {
handle: image.map(|image| ui.add_texture(image)),
}
}
pub trait LoadableImage {
fn get_image(self) -> Result<DynamicImage, String>;
}
impl LoadableImage for &str {
fn get_image(self) -> Result<DynamicImage, String> {
image::open(self).map_err(|e| format!("{e:?}"))
}
}
impl LoadableImage for String {
fn get_image(self) -> Result<DynamicImage, String> {
image::open(self).map_err(|e| format!("{e:?}"))
}
}
impl<const LEN: usize> LoadableImage for &[u8; LEN] {
fn get_image(self) -> Result<DynamicImage, String> {
image::load_from_memory(self).map_err(|e| format!("{e:?}"))
}
}
impl LoadableImage for DynamicImage {
fn get_image(self) -> Result<DynamicImage, String> {
Ok(self)
}
}

View File

@@ -1,4 +1,5 @@
mod frame; mod frame;
mod image;
mod num; mod num;
mod rect; mod rect;
mod sense; mod sense;
@@ -7,6 +8,7 @@ mod stack;
mod trait_fns; mod trait_fns;
pub use frame::*; pub use frame::*;
pub use image::*;
pub use num::*; pub use num::*;
pub use rect::*; pub use rect::*;
pub use sense::*; pub use sense::*;

View File

@@ -1,4 +1,4 @@
use crate::{primitive::RoundedRectData, Painter, UiNum, UiColor, Widget}; use crate::{Painter, UiColor, UiNum, Widget, render::RectPrimitive};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Rect { pub struct Rect {
@@ -29,7 +29,7 @@ impl Rect {
impl<Ctx> Widget<Ctx> for Rect { impl<Ctx> Widget<Ctx> for Rect {
fn draw(&self, painter: &mut Painter<Ctx>) { fn draw(&self, painter: &mut Painter<Ctx>) {
painter.write(RoundedRectData { painter.write(RectPrimitive {
color: self.color, color: self.color,
radius: self.radius, radius: self.radius,
thickness: self.thickness, thickness: self.thickness,

View File

@@ -1,4 +1,4 @@
use crate::{Dir, Painter, Sign, UiNum, UiRegion, UIScalar, Widget, WidgetId}; use crate::{Dir, Painter, Sign, UIScalar, UiNum, UiRegion, Widget, WidgetId};
pub struct Span { pub struct Span {
pub children: Vec<(WidgetId, SpanLen)>, pub children: Vec<(WidgetId, SpanLen)>,

View File

@@ -7,7 +7,7 @@ pub struct Stack {
impl<Ctx> Widget<Ctx> for Stack { impl<Ctx> Widget<Ctx> for Stack {
fn draw(&self, painter: &mut crate::Painter<Ctx>) { fn draw(&self, painter: &mut crate::Painter<Ctx>) {
for child in &self.children { for child in &self.children {
painter.draw_within(child, painter.region); painter.draw(child);
} }
} }
} }

View File

@@ -1,19 +1,21 @@
mod color; mod color;
mod id;
mod painter; mod painter;
mod region; mod region;
mod sense; mod sense;
mod texture;
mod ui; mod ui;
mod vec2; mod vec2;
mod widget; mod widget;
mod id;
pub use color::*; pub use color::*;
pub use id::*;
pub use painter::*; pub use painter::*;
pub use region::*; pub use region::*;
pub use sense::*; pub use sense::*;
pub use texture::*;
pub use ui::*; pub use ui::*;
pub use vec2::*; pub use vec2::*;
pub use widget::*; pub use widget::*;
pub use id::*;
pub type UiColor = Color<u8>; pub type UiColor = Color<u8>;

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
ActiveSensors, SensorMap, UiRegion, WidgetId, Widgets, ActiveSensors, SensorMap, TextureHandle, UiRegion, WidgetId, Widgets,
primitive::{Primitive, Primitives}, render::{Primitive, Primitives},
}; };
pub struct Painter<'a, Ctx: 'static> { pub struct Painter<'a, Ctx: 'static> {
@@ -32,7 +32,9 @@ impl<'a, Ctx> Painter<'a, Ctx> {
self.primitives.write(data, self.region); self.primitives.write(data, self.region);
} }
pub fn draw<W>(&mut self, id: &WidgetId<W>) /// Draws a widget but does NOT maintain the region.
/// Useful if you only have a single widget to draw.
pub fn draw_inner<W>(&mut self, id: &WidgetId<W>)
where where
Ctx: 'static, Ctx: 'static,
{ {
@@ -42,16 +44,34 @@ impl<'a, Ctx> Painter<'a, Ctx> {
self.nodes.get_dyn(id).draw(self); self.nodes.get_dyn(id).draw(self);
} }
pub fn draw_within(&mut self, node: &WidgetId, region: UiRegion) /// Draws a widget and maintains the original region.
/// Useful if you need to draw multiple overlapping child widgets.
pub fn draw<W>(&mut self, id: &WidgetId<W>)
where
Ctx: 'static,
{
let old = self.region;
self.draw_inner(id);
self.region = old;
}
/// Draws a widget within an inner region and maintains the original region.
/// Useful if you need to draw multiple child widgets in select areas within the original
/// region.
pub fn draw_within(&mut self, id: &WidgetId, region: UiRegion)
where where
Ctx: 'static, Ctx: 'static,
{ {
let old = self.region; let old = self.region;
self.region.select(&region); self.region.select(&region);
self.draw(node); self.draw_inner(id);
self.region = old; self.region = old;
} }
pub fn draw_texture(&mut self, handle: &TextureHandle) {
self.write(handle.inner);
}
pub fn finish(self) -> Primitives { pub fn finish(self) -> Primitives {
self.primitives self.primitives
} }

48
src/layout/texture.rs Normal file
View File

@@ -0,0 +1,48 @@
use image::DynamicImage;
use crate::render::TexturePrimitive;
/// TODO: proper resource management
pub struct TextureHandle {
pub inner: TexturePrimitive,
}
/// a texture manager for a ui
/// note that this is heavily oriented towards wgpu's renderer so the primitives don't need mapped
#[derive(Default)]
pub struct Textures {
/// TODO: these are images, not views rn
views: Vec<DynamicImage>,
changed: bool,
}
pub struct TextureUpdates<'a> {
pub images: Option<&'a [DynamicImage]>,
}
impl Textures {
pub fn add(&mut self, image: DynamicImage) -> TextureHandle {
let view_idx = self.views.len() as u32;
self.views.push(image);
// 0 == default in renderer; TODO: actually create samplers here
let sampler_idx = 0;
self.changed = true;
TextureHandle {
inner: TexturePrimitive {
view_idx,
sampler_idx,
},
}
}
pub fn updates(&mut self) -> TextureUpdates<'_> {
if self.changed {
self.changed = false;
TextureUpdates {
images: Some(&self.views),
}
} else {
TextureUpdates { images: None }
}
}
}

View File

@@ -1,7 +1,10 @@
use image::DynamicImage;
use crate::{ use crate::{
ActiveSensors, HashMap, Painter, SensorMap, Widget, WidgetId, WidgetLike, ActiveSensors, HashMap, Painter, SensorMap, TextureHandle, TextureUpdates, Textures, Widget,
primitive::Primitives, WidgetId, WidgetLike,
util::{IDTracker, Id}, render::Primitives,
util::{Id, IdTracker},
}; };
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
@@ -10,20 +13,24 @@ use std::{
}; };
pub struct Ui<Ctx> { pub struct Ui<Ctx> {
ids: IDTracker,
base: Option<WidgetId>, base: Option<WidgetId>,
widgets: Widgets<Ctx>, widgets: Widgets<Ctx>,
updates: Vec<WidgetId>, updates: Vec<WidgetId>,
del_recv: Receiver<Id>, del_recv: Receiver<Id>,
del_send: Sender<Id>, del_send: Sender<Id>,
primitives: Primitives,
textures: Textures,
full_redraw: bool,
pub(super) active_sensors: ActiveSensors, pub(super) active_sensors: ActiveSensors,
pub(super) sensor_map: SensorMap<Ctx>, pub(super) sensor_map: SensorMap<Ctx>,
primitives: Primitives,
full_redraw: bool,
} }
#[derive(Default)] #[derive(Default)]
pub struct Widgets<Ctx>(HashMap<Id, Box<dyn Widget<Ctx>>>); pub struct Widgets<Ctx> {
ids: IdTracker,
map: HashMap<Id, Box<dyn Widget<Ctx>>>,
}
impl<Ctx> Ui<Ctx> { impl<Ctx> Ui<Ctx> {
pub fn add<W: Widget<Ctx>, Tag>( pub fn add<W: Widget<Ctx>, Tag>(
@@ -68,7 +75,15 @@ impl<Ctx> Ui<Ctx> {
} }
pub fn id<W: Widget<Ctx>>(&mut self) -> WidgetId<W> { pub fn id<W: Widget<Ctx>>(&mut self) -> WidgetId<W> {
WidgetId::new(self.ids.next(), TypeId::of::<W>(), self.del_send.clone()) WidgetId::new(
self.widgets.reserve(),
TypeId::of::<W>(),
self.del_send.clone(),
)
}
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
self.textures.add(image)
} }
pub fn redraw_all(&mut self, ctx: &mut Ctx) pub fn redraw_all(&mut self, ctx: &mut Ctx)
@@ -83,12 +98,12 @@ impl<Ctx> Ui<Ctx> {
&mut self.active_sensors, &mut self.active_sensors,
); );
if let Some(base) = &self.base { if let Some(base) = &self.base {
painter.draw(base); painter.draw_inner(base);
} }
self.primitives = painter.finish(); self.primitives = painter.finish();
} }
pub fn update(&mut self, ctx: &mut Ctx) -> Option<&Primitives> pub fn update(&mut self, ctx: &mut Ctx) -> UiRenderUpdates
where where
Ctx: 'static, Ctx: 'static,
{ {
@@ -99,14 +114,24 @@ impl<Ctx> Ui<Ctx> {
if self.full_redraw { if self.full_redraw {
self.redraw_all(ctx); self.redraw_all(ctx);
self.full_redraw = false; self.full_redraw = false;
return Some(&self.primitives); UiRenderUpdates {
primitives: Some(&self.primitives),
textures: self.textures.updates(),
} }
if self.updates.is_empty() { } else if self.updates.is_empty() {
return None; UiRenderUpdates {
primitives: None,
textures: self.textures.updates(),
} }
} else {
// TODO: partial updates
self.redraw_all(ctx); self.redraw_all(ctx);
self.updates.drain(..); self.updates.drain(..);
Some(&self.primitives) UiRenderUpdates {
primitives: Some(&self.primitives),
textures: self.textures.updates(),
}
}
} }
pub fn needs_redraw(&self) -> bool { pub fn needs_redraw(&self) -> bool {
@@ -135,39 +160,51 @@ impl<W: Widget<Ctx>, Ctx> IndexMut<&WidgetId<W>> for Ui<Ctx> {
impl<Ctx> Widgets<Ctx> { impl<Ctx> Widgets<Ctx> {
pub fn new() -> Self { pub fn new() -> Self {
Self(HashMap::new()) Self {
ids: IdTracker::default(),
map: HashMap::new(),
}
} }
pub fn get_dyn<W>(&self, id: &WidgetId<W>) -> &dyn Widget<Ctx> { pub fn get_dyn<W>(&self, id: &WidgetId<W>) -> &dyn Widget<Ctx> {
self.0.get(&id.id).unwrap().as_ref() self.map.get(&id.id).unwrap().as_ref()
} }
pub fn get<W: Widget<Ctx>>(&self, id: &WidgetId<W>) -> Option<&W> { pub fn get<W: Widget<Ctx>>(&self, id: &WidgetId<W>) -> Option<&W> {
self.0.get(&id.id).unwrap().as_any().downcast_ref() self.map.get(&id.id).unwrap().as_any().downcast_ref()
} }
pub fn get_mut<W: Widget<Ctx>>(&mut self, id: &WidgetId<W>) -> Option<&mut W> { pub fn get_mut<W: Widget<Ctx>>(&mut self, id: &WidgetId<W>) -> Option<&mut W> {
self.0.get_mut(&id.id).unwrap().as_any_mut().downcast_mut() self.map
.get_mut(&id.id)
.unwrap()
.as_any_mut()
.downcast_mut()
} }
pub fn insert(&mut self, id: Id, widget: impl Widget<Ctx>) { pub fn insert(&mut self, id: Id, widget: impl Widget<Ctx>) {
self.0.insert(id, Box::new(widget)); self.map.insert(id, Box::new(widget));
} }
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget<Ctx>>) { pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget<Ctx>>) {
self.0.insert(id, widget); self.map.insert(id, widget);
} }
pub fn delete(&mut self, id: Id) { pub fn delete(&mut self, id: Id) {
self.0.remove(&id); self.map.remove(&id);
self.ids.free(id);
}
pub fn reserve(&mut self) -> Id {
self.ids.next()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.map.len()
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.map.is_empty()
} }
} }
@@ -185,11 +222,11 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
fn default() -> Self { fn default() -> Self {
let (del_send, del_recv) = channel(); let (del_send, del_recv) = channel();
Self { Self {
ids: Default::default(),
base: Default::default(), base: Default::default(),
widgets: Widgets::new(), widgets: Widgets::new(),
updates: Default::default(), updates: Default::default(),
primitives: Default::default(), primitives: Default::default(),
textures: Textures::default(),
full_redraw: false, full_redraw: false,
active_sensors: Default::default(), active_sensors: Default::default(),
sensor_map: Default::default(), sensor_map: Default::default(),
@@ -198,3 +235,8 @@ impl<Ctx: 'static> Default for Ui<Ctx> {
} }
} }
} }
pub struct UiRenderUpdates<'a> {
pub primitives: Option<&'a Primitives>,
pub textures: TextureUpdates<'a>,
}

View File

@@ -2,7 +2,7 @@ use crate::{util::{impl_op, F32Util}, UiNum};
use std::ops::*; use std::ops::*;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vec2 { pub struct Vec2 {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
@@ -60,3 +60,9 @@ impl<T: UiNum, U: UiNum> From<(T, U)> for Vec2 {
} }
} }
} }
impl std::fmt::Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

View File

@@ -41,8 +41,7 @@ pub(crate) use WidgetFnRet;
impl<W: Widget<Ctx>, Ctx, F: FnOnce(&mut Ui<Ctx>) -> W> WidgetLike<Ctx, FnTag> for F { impl<W: Widget<Ctx>, Ctx, F: FnOnce(&mut Ui<Ctx>) -> W> WidgetLike<Ctx, FnTag> for F {
type Widget = W; type Widget = W;
fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> { fn add(self, ui: &mut Ui<Ctx>) -> WidgetId<W> {
let w = self(ui); self(ui).add(ui)
ui.add(w)
} }
} }

View File

@@ -5,14 +5,13 @@
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(negative_impls)] #![feature(negative_impls)]
mod layout;
mod render;
mod util;
mod core; mod core;
mod layout;
pub mod render;
mod util;
pub use layout::*;
pub use render::*;
pub use core::*; pub use core::*;
pub use layout::*;
pub type HashMap<K, V> = std::collections::HashMap<K, V>; pub type HashMap<K, V> = std::collections::HashMap<K, V>;
pub type HashSet<K> = std::collections::HashSet<K>; pub type HashSet<K> = std::collections::HashSet<K>;

View File

@@ -1,6 +1,8 @@
use std::num::NonZero;
use crate::{ use crate::{
primitive::{PrimitiveBuffers, Primitives}, Ui, UiRenderUpdates,
render::{data::PrimitiveInstance, util::ArrBuf}, render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf},
}; };
use data::WindowUniform; use data::WindowUniform;
use wgpu::{ use wgpu::{
@@ -10,41 +12,55 @@ use wgpu::{
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
mod data; mod data;
pub mod primitive; mod primitive;
mod texture;
mod util; mod util;
pub use primitive::*;
const SHAPE_SHADER: &str = include_str!("./shader.wgsl"); const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
pub struct UIRenderNode { pub struct UiRenderer {
layout0: BindGroupLayout, uniform_layout: BindGroupLayout,
group0: BindGroup, uniform_group: BindGroup,
primitive_layout: BindGroupLayout, primitive_layout: BindGroupLayout,
primitive_group: BindGroup, primitive_group: BindGroup,
rsc_layout: BindGroupLayout,
rsc_group: BindGroup,
pipeline: RenderPipeline, pipeline: RenderPipeline,
window_buffer: Buffer, window_buffer: Buffer,
instance: ArrBuf<PrimitiveInstance>, instance: ArrBuf<PrimitiveInstance>,
primitives: PrimitiveBuffers, primitives: PrimitiveBuffers,
limits: UiLimits,
textures: GpuTextures,
} }
impl UIRenderNode { impl UiRenderer {
pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) { pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) {
if self.instance.len() != 0 { if self.instance.len() == 0 {
return;
}
pass.set_pipeline(&self.pipeline); pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &self.group0, &[]); pass.set_bind_group(0, &self.uniform_group, &[]);
pass.set_bind_group(1, &self.primitive_group, &[]); pass.set_bind_group(1, &self.primitive_group, &[]);
pass.set_bind_group(2, &self.rsc_group, &[]);
pass.set_vertex_buffer(0, self.instance.buffer.slice(..)); pass.set_vertex_buffer(0, self.instance.buffer.slice(..));
pass.draw(0..4, 0..self.instance.len() as u32); pass.draw(0..4, 0..self.instance.len() as u32);
} }
}
pub fn update(&mut self, device: &Device, queue: &Queue, primitives: Option<&Primitives>) { pub fn update(&mut self, device: &Device, queue: &Queue, updates: UiRenderUpdates) {
if let Some(primitives) = primitives { if let Some(primitives) = updates.primitives {
self.instance.update(device, queue, &primitives.instances); self.instance.update(device, queue, &primitives.instances);
self.primitives.update(device, queue, &primitives.data); self.primitives.update(device, queue, &primitives.data);
self.primitive_group = self.primitive_group =
Self::primitive_group(device, &self.primitive_layout, self.primitives.buffers()) Self::primitive_group(device, &self.primitive_layout, self.primitives.buffers())
} }
if self.textures.apply(updates.textures) {
self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures)
}
} }
pub fn resize(&mut self, size: &PhysicalSize<u32>, queue: &Queue) { pub fn resize(&mut self, size: &PhysicalSize<u32>, queue: &Queue) {
@@ -55,7 +71,12 @@ impl UIRenderNode {
queue.write_buffer(&self.window_buffer, 0, bytemuck::cast_slice(slice)); queue.write_buffer(&self.window_buffer, 0, bytemuck::cast_slice(slice));
} }
pub fn new(device: &Device, config: &SurfaceConfiguration) -> Self { pub fn new(
device: &Device,
queue: &Queue,
config: &SurfaceConfiguration,
limits: UiLimits,
) -> Self {
let shader = device.create_shader_module(ShaderModuleDescriptor { let shader = device.create_shader_module(ShaderModuleDescriptor {
label: Some("UI Shape Shader"), label: Some("UI Shape Shader"),
source: ShaderSource::Wgsl(SHAPE_SHADER.into()), source: ShaderSource::Wgsl(SHAPE_SHADER.into()),
@@ -75,7 +96,7 @@ impl UIRenderNode {
); );
let primitives = PrimitiveBuffers::new(device); let primitives = PrimitiveBuffers::new(device);
let layout0 = device.create_bind_group_layout(&BindGroupLayoutDescriptor { let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry { entries: &[BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: ShaderStages::VERTEX, visibility: ShaderStages::VERTEX,
@@ -89,7 +110,7 @@ impl UIRenderNode {
label: Some("window"), label: Some("window"),
}); });
let group0 = Self::bind_group_0(device, &layout0, &window_buffer); let uniform_group = Self::bind_group_0(device, &uniform_layout, &window_buffer);
let primitive_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { let primitive_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &core::array::from_fn::<_, { PrimitiveBuffers::LEN }, _>(|i| { entries: &core::array::from_fn::<_, { PrimitiveBuffers::LEN }, _>(|i| {
@@ -110,9 +131,13 @@ impl UIRenderNode {
let primitive_group = let primitive_group =
Self::primitive_group(device, &primitive_layout, primitives.buffers()); Self::primitive_group(device, &primitive_layout, primitives.buffers());
let tex_manager = GpuTextures::new(device, queue);
let rsc_layout = Self::rsc_layout(device, &limits);
let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager);
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("UI Shape Pipeline Layout"), label: Some("UI Shape Pipeline Layout"),
bind_group_layouts: &[&layout0, &primitive_layout], bind_group_layouts: &[&uniform_layout, &primitive_layout, &rsc_layout],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
@@ -154,18 +179,22 @@ impl UIRenderNode {
}); });
Self { Self {
layout0, uniform_layout,
group0, uniform_group,
primitive_layout, primitive_layout,
primitive_group, primitive_group,
rsc_layout,
rsc_group,
pipeline, pipeline,
window_buffer, window_buffer,
instance, instance,
primitives, primitives,
limits,
textures: tex_manager,
} }
} }
pub fn bind_group_0( fn bind_group_0(
device: &Device, device: &Device,
layout: &BindGroupLayout, layout: &BindGroupLayout,
window_buffer: &Buffer, window_buffer: &Buffer,
@@ -180,18 +209,86 @@ impl UIRenderNode {
}) })
} }
pub fn primitive_group( fn primitive_group(
device: &Device, device: &Device,
layout: &BindGroupLayout, layout: &BindGroupLayout,
buffers: [&Buffer; PrimitiveBuffers::LEN], buffers: [(u32, &Buffer); PrimitiveBuffers::LEN],
) -> BindGroup { ) -> BindGroup {
device.create_bind_group(&BindGroupDescriptor { device.create_bind_group(&BindGroupDescriptor {
layout, layout,
entries: &buffers.each_ref().map(|b| BindGroupEntry { entries: &buffers.map(|(binding, buf)| BindGroupEntry {
binding: 0, binding,
resource: b.as_entire_binding(), resource: buf.as_entire_binding(),
}), }),
label: Some("ui primitives"), label: Some("ui primitives"),
}) })
} }
fn rsc_layout(device: &Device, limits: &UiLimits) -> BindGroupLayout {
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: Some(NonZero::new(limits.max_textures).unwrap()),
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
count: Some(NonZero::new(limits.max_samplers).unwrap()),
},
],
label: Some("ui rsc"),
})
}
fn rsc_group(
device: &Device,
layout: &BindGroupLayout,
tex_manager: &GpuTextures,
) -> BindGroup {
device.create_bind_group(&BindGroupDescriptor {
layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureViewArray(&tex_manager.views()),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
},
],
label: Some("ui rsc"),
})
}
}
pub struct UiLimits {
max_textures: u32,
max_samplers: u32,
}
impl Default for UiLimits {
fn default() -> Self {
Self {
max_textures: 100000,
max_samplers: 1000,
}
}
}
impl UiLimits {
pub fn max_binding_array_elements_per_shader_stage(&self) -> u32 {
self.max_textures + self.max_samplers
}
pub fn max_binding_array_sampler_elements_per_shader_stage(&self) -> u32 {
self.max_samplers
}
} }

View File

@@ -35,9 +35,9 @@ macro_rules! primitives {
impl PrimitiveBuffers { impl PrimitiveBuffers {
pub const LEN: usize = primitives!(@count $($name)*); pub const LEN: usize = primitives!(@count $($name)*);
pub fn buffers(&self) -> [&Buffer; Self::LEN] { pub fn buffers(&self) -> [(u32, &Buffer); Self::LEN] {
[ [
$(&self.$name.buffer)* $((<$ty>::BINDING, &self.$name.buffer),)*
] ]
} }
pub fn new(device: &Device) -> Self { pub fn new(device: &Device) -> Self {
@@ -46,7 +46,7 @@ macro_rules! primitives {
device, device,
BufferUsages::STORAGE | BufferUsages::COPY_DST, BufferUsages::STORAGE | BufferUsages::COPY_DST,
stringify!($name), stringify!($name),
))* ),)*
} }
} }
} }
@@ -62,14 +62,10 @@ macro_rules! primitives {
} }
)* )*
}; };
(@count $t1:tt, $($t:tt),+) => { 1 + gen!(@count $($t),+) }; (@count $t1:tt $($t:tt)+) => { 1 + primitives!(@count $($t),+) };
(@count $t:tt) => { 1 }; (@count $t:tt) => { 1 };
} }
primitives!(
rects: RoundedRectData => 0,
);
impl Primitives { impl Primitives {
pub fn write<P: Primitive>(&mut self, data: P, region: UiRegion) { pub fn write<P: Primitive>(&mut self, data: P, region: UiRegion) {
let vec = P::vec(&mut self.data); let vec = P::vec(&mut self.data);
@@ -83,11 +79,23 @@ impl Primitives {
} }
} }
primitives!(
rects: RectPrimitive => 0,
textures: TexturePrimitive => 1,
);
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct RoundedRectData { pub struct RectPrimitive {
pub color: Color<u8>, pub color: Color<u8>,
pub radius: f32, pub radius: f32,
pub thickness: f32, pub thickness: f32,
pub inner_radius: f32, pub inner_radius: f32,
} }
#[repr(C)]
#[derive(Copy, Clone)]
pub struct TexturePrimitive {
pub view_idx: u32,
pub sampler_idx: u32,
}

View File

@@ -1,9 +1,29 @@
const RECT: u32 = 0; const RECT: u32 = 0;
const TEXTURE: u32 = 1;
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> window: WindowUniform; var<uniform> window: WindowUniform;
@group(1) @binding(RECT) @group(1) @binding(RECT)
var<storage> rects: array<RoundedRect>; var<storage> rects: array<Rect>;
@group(1) @binding(TEXTURE)
var<storage> textures: array<TextureInfo>;
struct Rect {
color: u32,
radius: f32,
thickness: f32,
inner_radius: f32,
}
struct TextureInfo {
view_idx: u32,
sampler_idx: u32,
}
@group(2) @binding(0)
var views: binding_array<texture_2d<f32>>;
@group(2) @binding(1)
var samplers: binding_array<sampler>;
struct WindowUniform { struct WindowUniform {
dim: vec2<f32>, dim: vec2<f32>,
@@ -18,23 +38,18 @@ struct InstanceInput {
@location(5) idx: u32, @location(5) idx: u32,
} }
struct RoundedRect {
color: u32,
radius: f32,
thickness: f32,
inner_radius: f32,
}
struct VertexOutput { struct VertexOutput {
@location(0) top_left: vec2<f32>, @location(0) top_left: vec2<f32>,
@location(1) bot_right: vec2<f32>, @location(1) bot_right: vec2<f32>,
@location(2) binding: u32, @location(2) uv: vec2<f32>,
@location(3) idx: u32, @location(3) binding: u32,
@location(4) idx: u32,
@builtin(position) clip_position: vec4<f32>, @builtin(position) clip_position: vec4<f32>,
}; };
struct Region { struct Region {
pos: vec2<f32>, pos: vec2<f32>,
uv: vec2<f32>,
top_left: vec2<f32>, top_left: vec2<f32>,
bot_right: vec2<f32>, bot_right: vec2<f32>,
} }
@@ -50,12 +65,13 @@ fn vs_main(
let bot_right = in.bottom_right_anchor * window.dim + in.bottom_right_offset; let bot_right = in.bottom_right_anchor * window.dim + in.bottom_right_offset;
let size = bot_right - top_left; let size = bot_right - top_left;
var pos = top_left + vec2<f32>( let uv = vec2<f32>(
f32(vi % 2u), f32(vi % 2u),
f32(vi / 2u) f32(vi / 2u)
) * size; );
pos = pos / window.dim * 2.0 - 1.0; let pos = (top_left + uv * size) / window.dim * 2.0 - 1.0;
out.clip_position = vec4<f32>(pos.x, -pos.y, 0.0, 1.0); out.clip_position = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
out.uv = uv;
out.binding = in.binding; out.binding = in.binding;
out.idx = in.idx; out.idx = in.idx;
out.top_left = top_left; out.top_left = top_left;
@@ -69,19 +85,27 @@ fn fs_main(
in: VertexOutput in: VertexOutput
) -> @location(0) vec4<f32> { ) -> @location(0) vec4<f32> {
let pos = in.clip_position.xy; let pos = in.clip_position.xy;
let region = Region(pos, in.top_left, in.bot_right); let region = Region(pos, in.uv, in.top_left, in.bot_right);
let i = in.idx; let i = in.idx;
switch in.binding { switch in.binding {
case RECT: { case RECT: {
return draw_rounded_rect(region, rects[i]); return draw_rounded_rect(region, rects[i]);
} }
case TEXTURE: {
return draw_texture(region, textures[i]);
}
default: { default: {
return vec4(1.0, 0.0, 1.0, 1.0); return vec4(1.0, 0.0, 1.0, 1.0);
} }
} }
} }
fn draw_rounded_rect(region: Region, rect: RoundedRect) -> vec4<f32> { // TODO: this seems really inefficient (per frag indexing)?
fn draw_texture(region: Region, info: TextureInfo) -> vec4<f32> {
return textureSample(views[info.view_idx], samplers[info.sampler_idx], region.uv);
}
fn draw_rounded_rect(region: Region, rect: Rect) -> vec4<f32> {
var color = read_color(rect.color); var color = read_color(rect.color);
let edge = 0.5; let edge = 0.5;
@@ -106,7 +130,7 @@ fn distance_from_rect(pixel_pos: vec2<f32>, rect_center: vec2<f32>, rect_corner:
let p = pixel_pos - rect_center; let p = pixel_pos - rect_center;
// vec from inner rect corner to pixel // vec from inner rect corner to pixel
let q = abs(p) - (rect_corner - radius); let q = abs(p) - (rect_corner - radius);
return length(max(q, vec2<f32>(0.0, 0.0))) - radius; return length(max(q, vec2(0.0))) - radius;
} }
fn read_color(c: u32) -> vec4<f32> { fn read_color(c: u32) -> vec4<f32> {

98
src/render/texture.rs Normal file
View File

@@ -0,0 +1,98 @@
use image::{DynamicImage, EncodableLayout};
use wgpu::{util::DeviceExt, *};
use crate::TextureUpdates;
pub struct GpuTextures {
device: Device,
queue: Queue,
views: Vec<TextureView>,
samplers: Vec<Sampler>,
no_views: Vec<TextureView>,
}
impl GpuTextures {
pub fn apply(&mut self, updates: TextureUpdates) -> bool {
if let Some(images) = updates.images {
for img in images {
self.add_view(img);
}
true
} else {
false
}
}
fn add_view(&mut self, image: &DynamicImage) {
let image = image.to_rgba8();
let (width, height) = image.dimensions();
let texture = self.device.create_texture_with_data(
&self.queue,
&TextureDescriptor {
label: None,
size: Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
},
wgt::TextureDataOrder::MipMajor,
image.as_bytes(),
);
let view = texture.create_view(&TextureViewDescriptor::default());
self.views.push(view);
}
pub fn new(device: &Device, queue: &Queue) -> Self {
Self {
device: device.clone(),
queue: queue.clone(),
views: Vec::new(),
samplers: vec![default_sampler(device)],
no_views: vec![null_texture_view(device)],
}
}
pub fn views(&self) -> Vec<&TextureView> {
if self.views.is_empty() {
&self.no_views
} else {
&self.views
}
.iter()
.by_ref()
.collect()
}
pub fn samplers(&self) -> Vec<&Sampler> {
self.samplers.iter().by_ref().collect()
}
}
pub fn null_texture_view(device: &Device) -> TextureView {
device
.create_texture(&TextureDescriptor {
label: Some("null"),
size: Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
})
.create_view(&TextureViewDescriptor::default())
}
pub fn default_sampler(device: &Device) -> Sampler {
device.create_sampler(&SamplerDescriptor::default())
}

View File

@@ -32,7 +32,7 @@ impl<T: Pod> ArrBuf<T> {
fn init_buf(device: &Device, size: usize, usage: BufferUsages, label: &'static str) -> Buffer { fn init_buf(device: &Device, size: usize, usage: BufferUsages, label: &'static str) -> Buffer {
let mut size = size as u64; let mut size = size as u64;
if usage.contains(BufferUsages::STORAGE) { if usage.contains(BufferUsages::STORAGE) {
size = size.max(1); size = size.max(std::mem::size_of::<T>() as u64);
} }
device.create_buffer(&BufferDescriptor { device.create_buffer(&BufferDescriptor {
label: Some(label), label: Some(label),

View File

@@ -1,4 +1,4 @@
use gui::Ui; use ui::Ui;
use winit::{ use winit::{
application::ApplicationHandler, application::ApplicationHandler,
event::WindowEvent, event::WindowEvent,

BIN
src/testing/assets/sungals.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,4 +1,4 @@
use gui::{CursorState, Vec2}; use ui::{CursorState, Vec2};
use winit::event::WindowEvent; use winit::event::WindowEvent;
use crate::testing::Client; use crate::testing::Client;

View File

@@ -1,8 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use app::App; use app::App;
use gui::*;
use render::Renderer; use render::Renderer;
use ui::*;
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window}; use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
use crate::testing::input::Input; use crate::testing::input::Input;
@@ -76,6 +76,8 @@ impl Client {
let span_add_test = ui.add(Span::empty(Dir::RIGHT).id(&span_add)); let span_add_test = ui.add(Span::empty(Dir::RIGHT).id(&span_add));
let main: WidgetId<Regioned> = ui.id(); let main: WidgetId<Regioned> = ui.id();
let image_test = ui.add(image(include_bytes!("assets/sungals.png")));
fn switch_button<To>( fn switch_button<To>(
color: UiColor, color: UiColor,
main: &WidgetId<Regioned>, main: &WidgetId<Regioned>,
@@ -104,8 +106,9 @@ impl Client {
switch_button(UiColor::RED, &main, &pad_test), switch_button(UiColor::RED, &main, &pad_test),
switch_button(UiColor::GREEN, &main, &span_test), switch_button(UiColor::GREEN, &main, &span_test),
switch_button(UiColor::BLUE, &main, &span_add_test), switch_button(UiColor::BLUE, &main, &span_add_test),
switch_button(UiColor::MAGENTA, &main, &image_test),
) )
.span(Dir::RIGHT, [1, 1, 1]), .span(Dir::RIGHT, [1, 1, 1, 1]),
); );
let test_button = Rect::new(Color::PURPLE) let test_button = Rect::new(Color::PURPLE)
.radius(30) .radius(30)
@@ -156,8 +159,8 @@ impl Client {
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
let primitives = ui.update(self); let updates = ui.update(self);
self.renderer.update(primitives); self.renderer.update(updates);
self.renderer.draw() self.renderer.draw()
} }
WindowEvent::Resized(size) => self.renderer.resize(&size), WindowEvent::Resized(size) => self.renderer.resize(&size),

View File

@@ -1,48 +1,51 @@
use gui::{UIRenderNode, primitive::Primitives};
use pollster::FutureExt; use pollster::FutureExt;
use std::sync::Arc; use std::sync::Arc;
use wgpu::util::StagingBelt; use ui::{
UiRenderUpdates,
render::{UiLimits, UiRenderer},
};
use wgpu::{util::StagingBelt, *};
use winit::{dpi::PhysicalSize, window::Window}; use winit::{dpi::PhysicalSize, window::Window};
pub const CLEAR_COLOR: wgpu::Color = wgpu::Color::BLACK; pub const CLEAR_COLOR: Color = Color::BLACK;
pub struct Renderer { pub struct Renderer {
window: Arc<Window>, window: Arc<Window>,
surface: wgpu::Surface<'static>, surface: Surface<'static>,
device: wgpu::Device, device: Device,
queue: wgpu::Queue, queue: Queue,
config: wgpu::SurfaceConfiguration, config: SurfaceConfiguration,
encoder: wgpu::CommandEncoder, encoder: CommandEncoder,
staging_belt: StagingBelt, staging_belt: StagingBelt,
ui_node: UIRenderNode, ui: UiRenderer,
} }
impl Renderer { impl Renderer {
pub fn update(&mut self, primitives: Option<&Primitives>) { pub fn update(&mut self, updates: UiRenderUpdates) {
self.ui_node.update(&self.device, &self.queue, primitives); self.ui.update(&self.device, &self.queue, updates);
} }
pub fn draw(&mut self) { pub fn draw(&mut self) {
let output = self.surface.get_current_texture().unwrap(); let output = self.surface.get_current_texture().unwrap();
let view = output let view = output
.texture .texture
.create_view(&wgpu::TextureViewDescriptor::default()); .create_view(&TextureViewDescriptor::default());
let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device)); let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device));
{ {
let render_pass = &mut encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let render_pass = &mut encoder.begin_render_pass(&RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment { color_attachments: &[Some(RenderPassColorAttachment {
view: &view, view: &view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: Operations {
load: wgpu::LoadOp::Clear(CLEAR_COLOR), load: LoadOp::Clear(CLEAR_COLOR),
store: wgpu::StoreOp::Store, store: StoreOp::Store,
}, },
depth_slice: None, depth_slice: None,
})], })],
..Default::default() ..Default::default()
}); });
self.ui_node.draw(render_pass); self.ui.draw(render_pass);
} }
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));
@@ -55,11 +58,11 @@ impl Renderer {
self.config.width = size.width; self.config.width = size.width;
self.config.height = size.height; self.config.height = size.height;
self.surface.configure(&self.device, &self.config); self.surface.configure(&self.device, &self.config);
self.ui_node.resize(size, &self.queue); self.ui.resize(size, &self.queue);
} }
fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder { fn create_encoder(device: &Device) -> CommandEncoder {
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}) })
} }
@@ -67,8 +70,8 @@ impl Renderer {
pub fn new(window: Arc<Window>) -> Self { pub fn new(window: Arc<Window>) -> Self {
let size = window.inner_size(); let size = window.inner_size();
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { let instance = Instance::new(&InstanceDescriptor {
backends: wgpu::Backends::PRIMARY, backends: Backends::PRIMARY,
..Default::default() ..Default::default()
}); });
@@ -77,18 +80,28 @@ impl Renderer {
.expect("Could not create window surface!"); .expect("Could not create window surface!");
let adapter = instance let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions { .request_adapter(&RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(), power_preference: PowerPreference::default(),
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
force_fallback_adapter: false, force_fallback_adapter: false,
}) })
.block_on() .block_on()
.expect("Could not get adapter!"); .expect("Could not get adapter!");
let ui_limits = UiLimits::default();
let (device, queue) = adapter let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor { .request_device(&DeviceDescriptor {
required_features: wgpu::Features::empty(), required_features: Features::TEXTURE_BINDING_ARRAY
required_limits: wgpu::Limits::default(), | Features::PARTIALLY_BOUND_BINDING_ARRAY
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
required_limits: Limits {
max_binding_array_elements_per_shader_stage: ui_limits
.max_binding_array_elements_per_shader_stage(),
max_binding_array_sampler_elements_per_shader_stage: ui_limits
.max_binding_array_sampler_elements_per_shader_stage(),
..Default::default()
},
..Default::default() ..Default::default()
}) })
.block_on() .block_on()
@@ -102,12 +115,12 @@ impl Renderer {
.find(|f| f.is_srgb()) .find(|f| f.is_srgb())
.unwrap_or(surface_caps.formats[0]); .unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration { let config = SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: TextureUsages::RENDER_ATTACHMENT,
format: surface_format, format: surface_format,
width: size.width, width: size.width,
height: size.height, height: size.height,
present_mode: wgpu::PresentMode::AutoVsync, present_mode: PresentMode::AutoVsync,
alpha_mode: surface_caps.alpha_modes[0], alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2, desired_maximum_frame_latency: 2,
view_formats: vec![], view_formats: vec![],
@@ -118,7 +131,7 @@ impl Renderer {
let staging_belt = StagingBelt::new(4096 * 4); let staging_belt = StagingBelt::new(4096 * 4);
let encoder = Self::create_encoder(&device); let encoder = Self::create_encoder(&device);
let shape_pipeline = UIRenderNode::new(&device, &config); let shape_pipeline = UiRenderer::new(&device, &queue, &config, ui_limits);
Self { Self {
surface, surface,
@@ -127,7 +140,7 @@ impl Renderer {
config, config,
encoder, encoder,
staging_belt, staging_belt,
ui_node: shape_pipeline, ui: shape_pipeline,
window, window,
} }
} }

View File

@@ -7,12 +7,12 @@
pub struct Id(u64); pub struct Id(u64);
#[derive(Default)] #[derive(Default)]
pub struct IDTracker { pub struct IdTracker {
free: Vec<Id>, free: Vec<Id>,
cur: u64, cur: u64,
} }
impl IDTracker { impl IdTracker {
#[allow(clippy::should_implement_trait)] #[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Id { pub fn next(&mut self) -> Id {
if let Some(id) = self.free.pop() { if let Some(id) = self.free.pop() {