diff --git a/Cargo.lock b/Cargo.lock index 43dff4f0..b64b450c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,12 +52,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" -[[package]] -name = "allocator-api2" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" - [[package]] name = "android-activity" version = "0.6.0" @@ -76,7 +70,7 @@ dependencies = [ "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -154,9 +148,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "approx" @@ -190,7 +184,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -369,6 +363,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitstream-io" @@ -418,22 +415,22 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -450,9 +447,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "calloop" @@ -465,7 +462,7 @@ dependencies = [ "polling", "rustix", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -482,9 +479,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.37" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "jobserver", "libc", @@ -513,12 +510,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -554,9 +545,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "9560b07a799281c7e0958b9296854d6fafd4c5f31444a7e5bb1ad6dde5ccf1bd" dependencies = [ "clap_builder", "clap_derive", @@ -564,9 +555,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "874e0dd3eb68bf99058751ac9712f622e61e6f393a94f7128fa26e3f02f5c7cd" dependencies = [ "anstream", "anstyle", @@ -576,14 +567,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -702,11 +693,27 @@ dependencies = [ "libc", ] +[[package]] +name = "craballoc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b378ae0de786477bac466f4ecd4a102402cd3a08cc05de38c0ae07443e9e1b0f" +dependencies = [ + "async-channel 1.9.0", + "bytemuck", + "crabslab", + "log", + "rustc-hash 1.1.0", + "snafu 0.8.5", + "tracing", + "wgpu", +] + [[package]] name = "crabslab" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f1b554aea54b251b8094d2e493b5b1003d43a759fc6e206e43e8ef0450a9b1" +checksum = "6d3486b5979b1c73ca6ba48f69e2abd7941dd72b0e725519d113aef61ff7a236" dependencies = [ "crabslab-derive", "futures-lite 1.13.0", @@ -722,7 +729,7 @@ checksum = "ef999dd82fff9dc1f2cf371c0f9a6315016b9562a04811fbefae2d80da6a1fad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -736,18 +743,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -764,9 +771,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunch" @@ -782,12 +789,12 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -805,7 +812,7 @@ dependencies = [ "dot2", "log", "rustc-hash 1.1.0", - "snafu", + "snafu 0.7.5", ] [[package]] @@ -889,12 +896,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -914,9 +921,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -925,11 +932,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -937,7 +944,8 @@ dependencies = [ name = "example" version = "0.1.0" dependencies = [ - "clap 4.5.23", + "clap 4.5.24", + "craballoc", "env_logger", "futures-lite 1.13.0", "gltf", @@ -962,7 +970,7 @@ version = "0.1.0" dependencies = [ "env_logger", "example", - "fastrand 2.2.0", + "fastrand 2.3.0", "log", "renderling", "winit", @@ -1013,15 +1021,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1043,9 +1051,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1057,6 +1065,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1075,7 +1089,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -1180,9 +1194,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.14.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ "js-sys", "slotmap", @@ -1214,7 +1228,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -1247,7 +1261,7 @@ dependencies = [ "glyph_brush_draw_cache", "glyph_brush_layout", "ordered-float", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "twox-hash", ] @@ -1262,7 +1276,7 @@ dependencies = [ "crossbeam-deque", "linked-hash-map", "rayon", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", ] [[package]] @@ -1303,19 +1317,19 @@ checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "log", "presser", - "thiserror", + "thiserror 1.0.69", "windows", ] [[package]] name = "gpu-descriptor" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ "bitflags 2.6.0", "gpu-descriptor-types", - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -1339,20 +1353,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", - "allocator-api2", + "foldhash", ] -[[package]] -name = "hashbrown" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - [[package]] name = "heck" version = "0.4.1" @@ -1445,7 +1452,7 @@ version = "0.1.0" dependencies = [ "glam", "image", - "snafu", + "snafu 0.7.5", ] [[package]] @@ -1456,12 +1463,12 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown", ] [[package]] @@ -1487,7 +1494,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -1518,9 +1525,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jni" @@ -1533,7 +1540,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -1561,10 +1568,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1599,9 +1607,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libfuzzer-sys" @@ -1615,9 +1623,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1637,7 +1645,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -1648,9 +1656,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litrs" @@ -1665,7 +1673,7 @@ dependencies = [ "async-fs", "js-sys", "send_wrapper", - "snafu", + "snafu 0.8.5", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1708,9 +1716,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bca95f9a4955b3e4a821fbbcd5edfbd9be2a9a50bb5758173e5358bfb4c623" +checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08" dependencies = [ "lyon_path", "num-traits", @@ -1784,9 +1792,8 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +version = "0.30.0" +source = "git+https://github.com/gfx-rs/metal-rs.git?rev=ef768ff9d7#ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" dependencies = [ "bitflags 2.6.0", "block", @@ -1815,9 +1822,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", "simd-adler32", @@ -1826,12 +1833,12 @@ dependencies = [ [[package]] name = "naga" version = "23.0.0" -source = "git+https://github.com/gfx-rs/wgpu.git#79a6f2cd3158ff1ae16b94fcf260cf52744c73fc" +source = "git+https://github.com/gfx-rs/wgpu.git#a8a91737b2d2f378976e292074c75817593a0224" dependencies = [ "arrayvec", "bit-set", "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "cfg_aliases", "codespan-reporting", "hexf-parse", "indexmap", @@ -1840,7 +1847,7 @@ dependencies = [ "rustc-hash 1.1.0", "spirv", "termcolor", - "thiserror", + "thiserror 2.0.9", "unicode-xid", ] @@ -1856,7 +1863,7 @@ dependencies = [ "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1923,7 +1930,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -1974,7 +1981,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -2206,9 +2213,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.5.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ "num-traits", ] @@ -2219,7 +2226,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" dependencies = [ - "ttf-parser 0.25.0", + "ttf-parser 0.25.1", ] [[package]] @@ -2246,7 +2253,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -2259,9 +2266,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" @@ -2281,29 +2288,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "piper" @@ -2312,7 +2319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-io", ] @@ -2324,9 +2331,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.14" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2409,7 +2416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -2438,9 +2445,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2617,7 +2624,7 @@ dependencies = [ "rand_chacha 0.3.1", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -2683,9 +2690,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -2704,9 +2711,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2732,14 +2739,15 @@ dependencies = [ "assert_approx_eq", "async-channel 1.9.0", "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases", + "craballoc", "crabslab", "crunch", "ctor", "dagga", "env_logger", "example", - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-lite 1.13.0", "glam", "gltf", @@ -2756,7 +2764,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "serde_json", - "snafu", + "snafu 0.8.5", "spirv-std", "ttf-parser 0.20.0", "wgpu", @@ -2778,6 +2786,7 @@ dependencies = [ name = "renderling_ui" version = "0.3.5" dependencies = [ + "craballoc", "crabslab", "ctor", "env_logger", @@ -2791,7 +2800,7 @@ dependencies = [ "pretty_assertions", "renderling", "rustc-hash 1.1.0", - "snafu", + "snafu 0.8.5", "wgpu", ] @@ -2809,21 +2818,21 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2883,29 +2892,29 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -2981,7 +2990,7 @@ dependencies = [ "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -3008,7 +3017,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "doc-comment", - "snafu-derive", + "snafu-derive 0.7.5", +] + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive 0.8.5", ] [[package]] @@ -3023,6 +3041,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -3035,7 +3065,7 @@ dependencies = [ [[package]] name = "spirv-std" version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu#565db88a8c9022ffcf23d85e369a2595aa4e81cd" +source = "git+https://github.com/Rust-GPU/rust-gpu#562dff9176f63b318e9b34620ffacb8aa89dae9a" dependencies = [ "bitflags 1.3.2", "glam", @@ -3047,18 +3077,18 @@ dependencies = [ [[package]] name = "spirv-std-macros" version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu#565db88a8c9022ffcf23d85e369a2595aa4e81cd" +source = "git+https://github.com/Rust-GPU/rust-gpu#562dff9176f63b318e9b34620ffacb8aa89dae9a" dependencies = [ "proc-macro2", "quote", "spirv-std-types", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] name = "spirv-std-types" version = "0.9.0" -source = "git+https://github.com/Rust-GPU/rust-gpu#565db88a8c9022ffcf23d85e369a2595aa4e81cd" +source = "git+https://github.com/Rust-GPU/rust-gpu#562dff9176f63b318e9b34620ffacb8aa89dae9a" [[package]] name = "static_assertions" @@ -3097,9 +3127,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -3149,7 +3179,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", ] [[package]] @@ -3160,7 +3199,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", ] [[package]] @@ -3235,19 +3285,34 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] [[package]] name = "ttf-parser" @@ -3257,24 +3322,24 @@ checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "ttf-parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "twox-hash" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6db6856664807f43c17fbaf2718e2381ac1476a449aa104f5f64622defa1245" +checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908" dependencies = [ "rand 0.8.5", ] [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -3359,9 +3424,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3370,36 +3435,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3407,30 +3472,29 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" -version = "0.3.45" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426" +checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" dependencies = [ - "console_error_panic_hook", "js-sys", "minicov", "scoped-tls", @@ -3441,13 +3505,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.45" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" +checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -3561,9 +3625,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -3587,11 +3651,11 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "23.0.0" -source = "git+https://github.com/gfx-rs/wgpu.git#79a6f2cd3158ff1ae16b94fcf260cf52744c73fc" +version = "23.0.1" +source = "git+https://github.com/gfx-rs/wgpu.git#a8a91737b2d2f378976e292074c75817593a0224" dependencies = [ "arrayvec", - "cfg_aliases 0.1.1", + "cfg_aliases", "document-features", "js-sys", "log", @@ -3611,14 +3675,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "23.0.0" -source = "git+https://github.com/gfx-rs/wgpu.git#79a6f2cd3158ff1ae16b94fcf260cf52744c73fc" +version = "23.0.1" +source = "git+https://github.com/gfx-rs/wgpu.git#a8a91737b2d2f378976e292074c75817593a0224" dependencies = [ "arrayvec", "bit-vec", "bitflags 2.6.0", "bytemuck", - "cfg_aliases 0.1.1", + "cfg_aliases", "document-features", "indexmap", "log", @@ -3629,15 +3693,15 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror", + "thiserror 2.0.9", "wgpu-hal", "wgpu-types", ] [[package]] name = "wgpu-hal" -version = "23.0.0" -source = "git+https://github.com/gfx-rs/wgpu.git#79a6f2cd3158ff1ae16b94fcf260cf52744c73fc" +version = "23.0.1" +source = "git+https://github.com/gfx-rs/wgpu.git#a8a91737b2d2f378976e292074c75817593a0224" dependencies = [ "android_system_properties", "arrayvec", @@ -3646,7 +3710,7 @@ dependencies = [ "bitflags 2.6.0", "block", "bytemuck", - "cfg_aliases 0.1.1", + "cfg_aliases", "core-graphics-types", "glow", "glutin_wgl_sys", @@ -3663,6 +3727,7 @@ dependencies = [ "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", + "ordered-float", "parking_lot", "profiling", "range-alloc", @@ -3670,7 +3735,7 @@ dependencies = [ "renderdoc-sys", "rustc-hash 1.1.0", "smallvec", - "thiserror", + "thiserror 2.0.9", "wasm-bindgen", "web-sys", "wgpu-types", @@ -3681,7 +3746,7 @@ dependencies = [ [[package]] name = "wgpu-types" version = "23.0.0" -source = "git+https://github.com/gfx-rs/wgpu.git#79a6f2cd3158ff1ae16b94fcf260cf52744c73fc" +source = "git+https://github.com/gfx-rs/wgpu.git#a8a91737b2d2f378976e292074c75817593a0224" dependencies = [ "bitflags 2.6.0", "js-sys", @@ -3750,7 +3815,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -3761,7 +3826,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -3990,9 +4055,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.5" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" dependencies = [ "ahash", "android-activity", @@ -4001,7 +4066,7 @@ dependencies = [ "block2", "bytemuck", "calloop", - "cfg_aliases 0.2.1", + "cfg_aliases", "concurrent-queue", "core-foundation", "core-graphics", @@ -4042,9 +4107,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] @@ -4114,15 +4179,15 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.23" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" [[package]] name = "xtask" version = "0.1.0" dependencies = [ - "clap 4.5.23", + "clap 4.5.24", "env_logger", "log", "renderling_build", @@ -4152,7 +4217,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.95", ] [[package]] @@ -4172,9 +4237,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index eef64945..7e675896 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ +members = [ "crates/example", "crates/example-culling", "crates/example-wasm", @@ -19,9 +19,10 @@ resolver = "2" assert_approx_eq = "1.1.0" async-channel = "1.8" bytemuck = { version = "1.13.0", features = ["derive"] } -clap = { version = "4.5.23", features = ["derive"] } cfg_aliases = "0.2" -crabslab = { version = "0.6.2", default-features = false } +clap = { version = "4.5.23", features = ["derive"] } +craballoc = { version = "0.1.1" } +crabslab = { version = "0.6.3", default-features = false } ctor = "0.2.2" dagga = "0.2.1" env_logger = "0.10.0" @@ -38,8 +39,9 @@ rustc-hash = "1.1" serde = {version = "1.0", features = ["derive"]} serde_json = "1.0.117" send_wrapper = "0.6.0" -snafu = "0.7" -syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } +snafu = "0.8" +syn = { version = "2.0.49", features = ["full", "extra-traits", "parsing"] } +tracing = "0.1.41" wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = "0.3" diff --git a/crates/example/Cargo.toml b/crates/example/Cargo.toml index 9415791f..53dde42d 100644 --- a/crates/example/Cargo.toml +++ b/crates/example/Cargo.toml @@ -12,6 +12,7 @@ name = "example" [dependencies] clap = { version = "^4.3", features = ["derive"] } +craballoc.workspace = true #console-subscriber = "0.4.0" env_logger = {workspace=true} futures-lite = {workspace=true} diff --git a/crates/example/src/camera.rs b/crates/example/src/camera.rs index a1c29eee..82d9ae3e 100644 --- a/crates/example/src/camera.rs +++ b/crates/example/src/camera.rs @@ -1,11 +1,11 @@ //! Camera control. use std::str::FromStr; +use craballoc::prelude::Hybrid; use renderling::{ bvol::Aabb, camera::Camera, math::{Mat4, Quat, UVec2, Vec2, Vec3}, - slab::Hybrid, }; use winit::{event::KeyEvent, keyboard::Key}; diff --git a/crates/example/src/lib.rs b/crates/example/src/lib.rs index 444b61d5..20c05662 100644 --- a/crates/example/src/lib.rs +++ b/crates/example/src/lib.rs @@ -5,6 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; +use craballoc::prelude::{GpuArray, Hybrid}; use renderling::{ atlas::AtlasImage, bvol::{Aabb, BoundingSphere}, @@ -12,7 +13,6 @@ use renderling::{ math::{Mat4, UVec2, Vec2, Vec3, Vec4}, pbr::light::{DirectionalLight, Light}, skybox::Skybox, - slab::{GpuArray, Hybrid}, stage::{Animator, GltfDocument, Renderlet, Stage, Vertex}, Context, }; @@ -216,8 +216,7 @@ impl App { fn load_hdr_skybox(&mut self, bytes: Vec) { let img = AtlasImage::from_hdr_bytes(&bytes).unwrap(); - let (device, queue) = self.stage.get_device_and_queue_owned(); - let skybox = Skybox::new(&device, &queue, img, self.camera.id()); + let skybox = Skybox::new(self.stage.runtime(), img, self.camera.id()); self.skybox_image_bytes = Some(bytes); self.stage.set_skybox(skybox); } diff --git a/crates/renderling-ui/Cargo.toml b/crates/renderling-ui/Cargo.toml index 666f70c7..f5b0d357 100644 --- a/crates/renderling-ui/Cargo.toml +++ b/crates/renderling-ui/Cargo.toml @@ -10,6 +10,7 @@ categories = ["rendering", "game-development", "graphics"] readme = "README.md" [dependencies] +craballoc.workspace = true crabslab = {workspace = true} glyph_brush = "0.7.8" image = {workspace=true} diff --git a/crates/renderling-ui/src/lib.rs b/crates/renderling-ui/src/lib.rs index 3ded1f94..93fc3d40 100644 --- a/crates/renderling-ui/src/lib.rs +++ b/crates/renderling-ui/src/lib.rs @@ -28,13 +28,13 @@ //! Happy hacking! use std::sync::{Arc, RwLock}; +use craballoc::prelude::Hybrid; use crabslab::Id; use glyph_brush::ab_glyph; use renderling::{ atlas::AtlasTexture, camera::Camera, math::{Quat, UVec2, Vec2, Vec3Swizzles, Vec4}, - slab::Hybrid, stage::{NestedTransform, Renderlet, Stage}, transform::Transform, Context, diff --git a/crates/renderling-ui/src/path.rs b/crates/renderling-ui/src/path.rs index 9c92d554..f105322d 100644 --- a/crates/renderling-ui/src/path.rs +++ b/crates/renderling-ui/src/path.rs @@ -1,6 +1,7 @@ //! Path and builder. //! //! Path colors are sRGB. +use craballoc::prelude::{GpuArray, Hybrid}; use crabslab::Id; use lyon::{ path::traits::PathBuilder, @@ -11,7 +12,6 @@ use lyon::{ use renderling::{ math::{Vec2, Vec3, Vec3Swizzles, Vec4}, pbr::Material, - slab::{GpuArray, Hybrid}, stage::{Renderlet, Vertex}, }; diff --git a/crates/renderling-ui/src/text.rs b/crates/renderling-ui/src/text.rs index 03c9c9ad..c0b85fc4 100644 --- a/crates/renderling-ui/src/text.rs +++ b/crates/renderling-ui/src/text.rs @@ -8,6 +8,7 @@ use std::{ }; use ab_glyph::Rect; +use craballoc::prelude::{GpuArray, Hybrid}; use glyph_brush::*; pub use ab_glyph::FontArc; @@ -18,7 +19,6 @@ use renderling::{ atlas::AtlasTexture, math::{Vec2, Vec4}, pbr::Material, - slab::{GpuArray, Hybrid}, stage::{Renderlet, Vertex}, }; diff --git a/crates/renderling/Cargo.toml b/crates/renderling/Cargo.toml index 7462b9e1..2033d168 100644 --- a/crates/renderling/Cargo.toml +++ b/crates/renderling/Cargo.toml @@ -52,6 +52,7 @@ glam = { workspace = true, default-features = false, features = ["libm"] } [target.'cfg(not(target_arch = "spirv"))'.dependencies] async-channel = {workspace = true} bytemuck = {workspace = true} +craballoc.workspace = true crabslab = { workspace = true, features = ["default"] } dagga = {workspace=true} crunch = "0.5" diff --git a/crates/renderling/src/atlas/atlas_image.rs b/crates/renderling/src/atlas/atlas_image.rs index a9515ed5..ef6d3fb5 100644 --- a/crates/renderling/src/atlas/atlas_image.rs +++ b/crates/renderling/src/atlas/atlas_image.rs @@ -283,7 +283,6 @@ pub fn convert_to_rgba8_bytes( .chunks_exact(4) .flat_map(|p| { if let [r, g, b, a] = p { - todo!("f32_to_u8 erroneously used, use f16_to_u8 instead"); [f32_to_u8(*r), f32_to_u8(*g), f32_to_u8(*b), f32_to_u8(*a)] } else { unreachable!() diff --git a/crates/renderling/src/atlas/cpu.rs b/crates/renderling/src/atlas/cpu.rs index 9b1d6d5e..8ac50410 100644 --- a/crates/renderling/src/atlas/cpu.rs +++ b/crates/renderling/src/atlas/cpu.rs @@ -1,13 +1,15 @@ use core::ops::Deref; +use std::sync::{Arc, RwLock}; + +use craballoc::{ + prelude::{Hybrid, SlabAllocator, WeakHybrid}, + runtime::WgpuRuntime, +}; use glam::UVec2; use image::RgbaImage; use snafu::{prelude::*, OptionExt}; -use std::sync::{Arc, RwLock}; -use crate::{ - slab::{Hybrid, IsBuffer, SlabAllocator, WeakHybrid}, - texture::Texture, -}; +use crate::texture::Texture; use super::{ atlas_image::{convert_to_rgba8_bytes, AtlasImage}, @@ -49,15 +51,17 @@ impl InternalAtlasTexture { } fn has_external_references(&self) -> bool { - self.weak.weak_gpu.update.strong_count() > 0 + self.weak.has_external_references() } fn set(&mut self, at: AtlasTexture) { self.cache = at; if let Some(hy) = self.weak.upgrade() { hy.set(at); - } else if let Some(gpu) = self.weak.weak_gpu.upgrade() { + } else if let Some(gpu) = self.weak.weak_gpu().upgrade() { gpu.set(at) + } else { + log::warn!("could not set atlas texture, lost"); } } } @@ -102,7 +106,7 @@ enum AnotherPacking<'a> { Internal(InternalAtlasTexture), } -impl<'a> AnotherPacking<'a> { +impl AnotherPacking<'_> { fn size(&self) -> UVec2 { match self { AnotherPacking::Img { @@ -147,10 +151,11 @@ impl Atlas { /// Create the initial texture to use. fn create_texture( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, size: wgpu::Extent3d, ) -> Result { + let device = &runtime.as_ref().device; + let queue = &runtime.as_ref().queue; check_size(size)?; let texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("atlas texture"), @@ -206,13 +211,9 @@ impl Atlas { /// /// ## Panics /// Panics if `size` is not a power of two. - pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, - size: wgpu::Extent3d, - ) -> Result { + pub fn new(runtime: impl AsRef, size: wgpu::Extent3d) -> Result { log::trace!("creating new atlas with dimensions {size:?}"); - let texture = Self::create_texture(device, queue, size)?; + let texture = Self::create_texture(runtime, size)?; Ok(Self::new_with_texture(texture)) } @@ -243,9 +244,7 @@ impl Atlas { /// Any existing `Hybrid`s will be invalidated. pub fn set_images( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - slab: &SlabAllocator, + slab: &SlabAllocator, images: &[AtlasImage], ) -> Result>, AtlasError> { log::debug!("setting images"); @@ -258,7 +257,7 @@ impl Atlas { vec![Layer::default(); texture.texture.size().depth_or_array_layers as usize]; let _old_layers = std::mem::replace(layers, new_layers); } - self.add_images(device, queue, slab, images) + self.add_images(slab, images) } pub fn get_size(&self) -> wgpu::Extent3d { @@ -266,11 +265,10 @@ impl Atlas { self.texture_array.read().unwrap().texture.size() } + // TODO: Atlas should probably clone a reference to the runtime and the slab. pub fn add_images( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - slab: &SlabAllocator, + slab: &SlabAllocator, images: &[AtlasImage], ) -> Result>, AtlasError> { // UNWRAP: POP @@ -282,8 +280,7 @@ impl Atlas { .context(CannotPackTexturesSnafu { size: extent })?; let mut staged = StagedResources::try_staging( - device, - queue, + slab.runtime(), extent, newly_packed_layers, Some(slab), @@ -307,8 +304,7 @@ impl Atlas { /// Errors if `size` has a width or height that is not a power of two, or are unequal pub fn resize( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, extent: wgpu::Extent3d, ) -> Result<(), AtlasError> { let mut layers = self.layers.write().unwrap(); @@ -318,11 +314,10 @@ impl Atlas { pack_images(&layers, &[], extent).context(CannotPackTexturesSnafu { size: extent })?; let staged = StagedResources::try_staging( - device, - queue, + runtime, extent, newly_packed_layers, - None::<&SlabAllocator>, + None::<&SlabAllocator>, &texture_array, )?; @@ -337,7 +332,7 @@ impl Atlas { /// /// This removes any `TextureFrame`s that have no references and repacks the atlas /// if any were removed. - pub fn upkeep(&self, device: &wgpu::Device, queue: &wgpu::Queue) { + pub fn upkeep(&self, runtime: impl AsRef) { let mut total_dropped = 0; { let mut layers = self.layers.write().unwrap(); @@ -364,7 +359,7 @@ impl Atlas { log::trace!("repacking after dropping {total_dropped} frames from the atlas"); // UNWRAP: safe because we can only remove frames from the atlas, which should // only make it easier to pack. - self.resize(device, queue, self.get_size()).unwrap(); + self.resize(runtime.as_ref(), self.get_size()).unwrap(); } } @@ -381,9 +376,8 @@ impl Atlas { let tex = self.get_texture(); let size = tex.texture.size(); let buffer = Texture::read_from( + ctx, &tex.texture, - ctx.get_device(), - ctx.get_queue(), size.width as usize, size.height as usize, 4, @@ -463,18 +457,20 @@ struct StagedResources { impl StagedResources { /// Stage the packed images, copying them to the next texture. fn try_staging( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, extent: wgpu::Extent3d, newly_packed_layers: Vec>, - slab: Option<&SlabAllocator>, + slab: Option<&SlabAllocator>, old_texture_array: &Texture, ) -> Result { - let new_texture_array = Atlas::create_texture(device, queue, extent)?; + let runtime = runtime.as_ref(); + let new_texture_array = Atlas::create_texture(runtime, extent)?; let mut output = vec![]; - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("atlas staging"), - }); + let mut encoder = runtime + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("atlas staging"), + }); let mut temporary_layers = vec![Layer::default(); extent.depth_or_array_layers as usize]; for (layer_index, packed_items) in newly_packed_layers.into_iter().enumerate() { if packed_items.items.is_empty() { @@ -532,15 +528,15 @@ impl StagedResources { log::trace!(" size: {size:?}"); // write the new image from the CPU to the new texture - queue.write_texture( - wgpu::ImageCopyTextureBase { + runtime.queue.write_texture( + wgpu::TexelCopyTextureInfo { texture: &new_texture_array.texture, mip_level: 0, origin, aspect: wgpu::TextureAspect::All, }, &bytes, - wgpu::ImageDataLayout { + wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(4 * size_px.x), rows_per_image: Some(size_px.y), @@ -555,7 +551,7 @@ impl StagedResources { // copy the frame from the old texture to the new texture // in a new destination encoder.copy_texture_to_texture( - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &old_texture_array.texture, mip_level: 0, origin: wgpu::Origin3d { @@ -565,7 +561,7 @@ impl StagedResources { }, aspect: wgpu::TextureAspect::All, }, - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &new_texture_array.texture, mip_level: 0, origin: wgpu::Origin3d { @@ -591,7 +587,7 @@ impl StagedResources { } } } - queue.submit(Some(encoder.finish())); + runtime.queue.submit(Some(encoder.finish())); Ok(Self { texture: new_texture_array, @@ -953,7 +949,7 @@ mod test { frames.pop(); frames.pop(); - stage.atlas.upkeep(&stage.device, &stage.queue); + stage.atlas.upkeep(&ctx); assert_eq!(4, stage.atlas.len()); } } diff --git a/crates/renderling/src/bloom/cpu.rs b/crates/renderling/src/bloom/cpu.rs index 678f936d..1ad04d13 100644 --- a/crates/renderling/src/bloom/cpu.rs +++ b/crates/renderling/src/bloom/cpu.rs @@ -2,13 +2,14 @@ use core::ops::Deref; use std::sync::{Arc, RwLock}; +use craballoc::{ + prelude::{Hybrid, HybridArray, SlabAllocator}, + runtime::WgpuRuntime, +}; use crabslab::Id; use glam::{UVec2, Vec2}; -use crate::{ - slab::{Hybrid, HybridArray, SlabAllocator}, - texture::{self, Texture}, -}; +use crate::texture::{self, Texture}; fn create_bindgroup_layout(device: &wgpu::Device, label: Option<&str>) -> wgpu::BindGroupLayout { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -140,26 +141,27 @@ fn config_resolutions(resolution: UVec2) -> impl Iterator { } fn create_texture( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, width: u32, height: u32, label: Option<&str>, extra_usages: wgpu::TextureUsages, ) -> texture::Texture { - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label, - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); + let sampler = runtime + .as_ref() + .device + .create_sampler(&wgpu::SamplerDescriptor { + label, + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); Texture::new_with( - device, - queue, + runtime, label, Some( wgpu::TextureUsages::RENDER_ATTACHMENT @@ -178,11 +180,7 @@ fn create_texture( ) } -fn create_textures( - device: &wgpu::Device, - queue: &wgpu::Queue, - resolution: UVec2, -) -> Vec { +fn create_textures(runtime: impl AsRef, resolution: UVec2) -> Vec { let resolutions = config_resolutions(resolution).collect::>(); log::trace!( "creating {} bloom textures at resolution {resolution}", @@ -200,8 +198,7 @@ fn create_textures( let title = format!("bloom texture[{i}]"); let label = Some(title.as_str()); let texture = create_texture( - device, - queue, + runtime.as_ref(), width, height, label, @@ -386,7 +383,7 @@ fn create_mix_bindgroup( /// Clones of [`Bloom`] all point to the same resources. #[derive(Clone)] pub struct Bloom { - slab: SlabAllocator, + slab: SlabAllocator, downsample_pixel_sizes: HybridArray, downsample_pipeline: Arc, @@ -405,47 +402,48 @@ pub struct Bloom { } impl Bloom { - pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, hdr_texture: &Texture) -> Self { + pub fn new(runtime: impl AsRef, hdr_texture: &Texture) -> Self { + let runtime = runtime.as_ref(); let resolution = UVec2::new(hdr_texture.width(), hdr_texture.height()); - let slab = SlabAllocator::default(); + let slab = + SlabAllocator::new_with_label(runtime, wgpu::BufferUsages::empty(), Some("bloom-slab")); let downsample_pixel_sizes = slab.new_array( config_resolutions(resolution).map(|r| 1.0 / Vec2::new(r.x as f32, r.y as f32)), ); let upsample_filter_radius = slab.new_value(1.0 / Vec2::new(resolution.x as f32, resolution.y as f32)); let mix_strength = slab.new_value(0.04f32); - let slab_buffer = slab.get_updated_buffer(( - device, - queue, - Some("bloom slab"), - wgpu::BufferUsages::empty(), - )); + let slab_buffer = slab.get_updated_buffer(); - let downsample_pipeline = Arc::new(create_bloom_downsample_pipeline(device)); - let upsample_pipeline = Arc::new(create_bloom_upsample_pipeline(device)); - let mix_pipeline = Arc::new(create_mix_pipeline(device)); + let downsample_pipeline = Arc::new(create_bloom_downsample_pipeline(&runtime.device)); + let upsample_pipeline = Arc::new(create_bloom_upsample_pipeline(&runtime.device)); + let mix_pipeline = Arc::new(create_mix_pipeline(&runtime.device)); let hdr_texture_downsample_bindgroup = create_bindgroup( - device, + &runtime.device, &downsample_pipeline.get_bind_group_layout(0), &slab_buffer, hdr_texture, ); let mix_texture = create_texture( - device, - queue, + runtime, resolution.x, resolution.y, Some("bloom mix"), wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::COPY_DST, ); - let textures = create_textures(device, queue, resolution); - let bindgroups = create_bindgroups(device, &downsample_pipeline, &slab_buffer, &textures); + let textures = create_textures(runtime, resolution); + let bindgroups = create_bindgroups( + &runtime.device, + &downsample_pipeline, + &slab_buffer, + &textures, + ); let mix_bindgroup = create_mix_bindgroup( - device, + &runtime.device, &mix_pipeline, &slab_buffer, hdr_texture, @@ -495,36 +493,34 @@ impl Bloom { } /// Recreates this bloom using the new HDR texture. - pub fn set_hdr_texture( - &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - hdr_texture: &Texture, - ) { + pub fn set_hdr_texture(&self, runtime: impl AsRef, hdr_texture: &Texture) { // UNWRAP: panic on purpose (here and on till the end of this fn) let slab_buffer = self.slab.get_buffer().unwrap(); let resolution = UVec2::new(hdr_texture.width(), hdr_texture.height()); + let runtime = runtime.as_ref(); + let textures = create_textures(runtime, resolution); - let textures = create_textures(device, queue, resolution); - - *self.bindgroups.write().unwrap() = - create_bindgroups(device, &self.downsample_pipeline, &slab_buffer, &textures); + *self.bindgroups.write().unwrap() = create_bindgroups( + &runtime.device, + &self.downsample_pipeline, + &slab_buffer, + &textures, + ); *self.hdr_texture_downsample_bindgroup.write().unwrap() = create_bindgroup( - device, + &runtime.device, &self.downsample_pipeline.get_bind_group_layout(0), &slab_buffer, hdr_texture, ); *self.mix_texture.write().unwrap() = create_texture( - device, - queue, + runtime, resolution.x, resolution.y, Some("bloom mix"), wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::COPY_DST, ); *self.mix_bindgroup.write().unwrap() = create_mix_bindgroup( - device, + &runtime.device, &self.mix_pipeline, &slab_buffer, hdr_texture, @@ -686,14 +682,7 @@ impl Bloom { pub fn bloom(&self, device: &wgpu::Device, queue: &wgpu::Queue) { assert!( - self.slab - .upkeep(( - device, - queue, - Some("bloom upkeep"), - wgpu::BufferUsages::empty(), - )) - .is_none(), + self.slab.upkeep().is_none(), "bloom slab buffer should never resize" ); self.render_downsamples(device, queue); diff --git a/crates/renderling/src/context.rs b/crates/renderling/src/context.rs index a745c7f8..cac93170 100644 --- a/crates/renderling/src/context.rs +++ b/crates/renderling/src/context.rs @@ -5,6 +5,7 @@ use std::{ sync::{Arc, RwLock}, }; +use craballoc::runtime::WgpuRuntime; use glam::{UVec2, UVec3}; use snafu::prelude::*; @@ -169,7 +170,7 @@ fn new_instance(backends: Option) -> wgpu::Instance { ); // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU let backends = backends.unwrap_or(wgpu::Backends::PRIMARY); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { backends, ..Default::default() }); @@ -295,8 +296,7 @@ pub(crate) enum FrameSurface { /// /// Returned by [`Context::get_next_frame`]. pub struct Frame { - pub(crate) device: Arc, - pub(crate) queue: Arc, + pub(crate) runtime: WgpuRuntime, pub(crate) surface: FrameSurface, } @@ -315,40 +315,32 @@ impl Frame { texture.create_view(&wgpu::TextureViewDescriptor { label: Some("Frame::view"), format: Some(format), - dimension: None, - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - mip_level_count: None, - base_array_layer: 0, - array_layer_count: None, + ..Default::default() }) } - pub fn copy_to_buffer( - &self, - device: &wgpu::Device, - queue: &wgpu::Queue, - width: u32, - height: u32, - ) -> CopiedTextureBuffer { + pub fn copy_to_buffer(&self, width: u32, height: u32) -> CopiedTextureBuffer { let dimensions = BufferDimensions::new(4, 1, width as usize, height as usize); // The output buffer lets us retrieve the self as an array - let buffer = device.create_buffer(&wgpu::BufferDescriptor { + let buffer = self.runtime.device.create_buffer(&wgpu::BufferDescriptor { label: Some("RenderTarget::copy_to_buffer"), size: (dimensions.padded_bytes_per_row * dimensions.height) as u64, usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("post render screen capture encoder"), - }); + let mut encoder = + self.runtime + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("post render screen capture encoder"), + }); let texture = self.texture(); // Copy the data from the surface texture to the buffer encoder.copy_texture_to_buffer( texture.as_image_copy(), - wgpu::ImageCopyBuffer { + wgpu::TexelCopyBufferInfo { buffer: &buffer, - layout: wgpu::ImageDataLayout { + layout: wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(dimensions.padded_bytes_per_row as u32), rows_per_image: None, @@ -361,7 +353,7 @@ impl Frame { }, ); - queue.submit(std::iter::once(encoder.finish())); + self.runtime.queue.submit(std::iter::once(encoder.finish())); CopiedTextureBuffer { dimensions, @@ -386,12 +378,12 @@ impl Frame { /// This operation can take a long time, depending on how big the screen is. pub fn read_image(&self) -> Result { let size = self.get_size(); - let buffer = self.copy_to_buffer(&self.device, &self.queue, size.x, size.y); + let buffer = self.copy_to_buffer(size.x, size.y); let is_srgb = self.texture().format().is_srgb(); let img = if is_srgb { - buffer.into_srgba(&self.device)? + buffer.into_srgba(&self.runtime.device)? } else { - buffer.into_linear_rgba(&self.device)? + buffer.into_linear_rgba(&self.runtime.device)? }; Ok(img) } @@ -407,9 +399,9 @@ impl Frame { /// This operation can take a long time, depending on how big the screen is. pub fn read_srgb_image(&self) -> Result { let size = self.get_size(); - let buffer = self.copy_to_buffer(&self.device, &self.queue, size.x, size.y); + let buffer = self.copy_to_buffer(size.x, size.y); log::trace!("read image has the format: {:?}", buffer.format); - buffer.into_srgba(&self.device) + buffer.into_srgba(&self.runtime.device) } /// Read the frame into an image. /// @@ -422,8 +414,8 @@ impl Frame { /// This operation can take a long time, depending on how big the screen is. pub fn read_linear_image(&self) -> Result { let size = self.get_size(); - let buffer = self.copy_to_buffer(&self.device, &self.queue, size.x, size.y); - buffer.into_linear_rgba(&self.device) + let buffer = self.copy_to_buffer(size.x, size.y); + buffer.into_linear_rgba(&self.runtime.device) } /// If self is `TargetFrame::Surface` this presents the surface frame. @@ -437,7 +429,7 @@ impl Frame { } } -/// Contains the adapter, device, queue and [`RenderTarget`]. +/// Contains the adapter, device, queue, [`RenderTarget`] and initial atlas sizing. /// /// A `Context` is created to initialize rendering to a window, canvas or /// texture. @@ -448,13 +440,18 @@ impl Frame { /// let ctx = Context::headless(100, 100); /// ``` pub struct Context { + runtime: WgpuRuntime, adapter: Arc, - device: Arc, - queue: Arc, render_target: RenderTarget, pub(crate) atlas_size: Arc>, } +impl AsRef for Context { + fn as_ref(&self) -> &WgpuRuntime { + &self.runtime + } +} + impl Context { pub fn new( target: RenderTarget, @@ -477,8 +474,10 @@ impl Context { })); Self { adapter, - device: device.into(), - queue: queue.into(), + runtime: WgpuRuntime { + device: device.into(), + queue: queue.into(), + }, render_target: target, atlas_size, } @@ -570,8 +569,8 @@ impl Context { } pub fn set_size(&mut self, size: UVec2) { - let (device, _) = self.get_device_and_queue_owned(); - self.render_target.resize(size.x, size.y, &device); + self.render_target + .resize(size.x, size.y, &self.runtime.device); } /// Convenience method for creating textures from an image buffer. @@ -585,11 +584,8 @@ impl Context { image::ImageBuffer>: image::GenericImage + std::ops::Deref, { let name = label.unwrap_or("unknown"); - let device = &self.device; - let queue = &self.queue; Texture::from_image_buffer( - device, - queue, + self, img, Some(&format!("Renderling::create_texture {}", name)), None, @@ -606,11 +602,11 @@ impl Context { } pub fn get_device(&self) -> &wgpu::Device { - &self.device + &self.runtime.device } pub fn get_queue(&self) -> &wgpu::Queue { - &self.queue + &self.runtime.queue } pub fn get_adapter(&self) -> &wgpu::Adapter { @@ -622,11 +618,6 @@ impl Context { self.adapter.clone() } - /// Returns a pair of the device and queue in an owned wrapper. - pub fn get_device_and_queue_owned(&self) -> (Arc, Arc) { - (self.device.clone(), self.queue.clone()) - } - pub fn get_render_target(&self) -> &RenderTarget { &self.render_target } @@ -643,8 +634,7 @@ impl Context { /// been acquired. pub fn get_next_frame(&self) -> Result { Ok(Frame { - device: self.device.clone(), - queue: self.queue.clone(), + runtime: self.runtime.clone(), surface: match &self.render_target.0 { RenderTargetInner::Surface { surface, .. } => { let surface_texture = surface.get_current_texture().context(SurfaceSnafu)?; diff --git a/crates/renderling/src/cull/cpu.rs b/crates/renderling/src/cull/cpu.rs index 5c369db9..1e47175f 100644 --- a/crates/renderling/src/cull/cpu.rs +++ b/crates/renderling/src/cull/cpu.rs @@ -1,14 +1,15 @@ //! CPU side of compute culling. +use craballoc::{ + prelude::{GpuArray, Hybrid, SlabAllocator, SlabAllocatorError}, + runtime::WgpuRuntime, +}; use crabslab::{Array, Slab}; use glam::UVec2; use snafu::{OptionExt, Snafu}; use std::sync::Arc; -use crate::{ - slab::{GpuArray, Hybrid, SlabAllocator, SlabAllocatorError}, - texture::Texture, -}; +use crate::texture::Texture; use super::DepthPyramidDescriptor; @@ -80,7 +81,8 @@ impl ComputeCulling { }) } - pub fn new(device: &wgpu::Device, size: UVec2, sample_count: u32) -> Self { + pub fn new(runtime: impl AsRef, size: UVec2, sample_count: u32) -> Self { + let device = &runtime.as_ref().device; let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Self::LABEL, entries: &[ @@ -135,7 +137,7 @@ impl ComputeCulling { pipeline, bindgroup_layout, bindgroup: None, - compute_depth_pyramid: ComputeDepthPyramid::new(device, size, sample_count), + compute_depth_pyramid: ComputeDepthPyramid::new(runtime, size, sample_count), } } @@ -173,18 +175,12 @@ impl ComputeCulling { depth_texture: &Texture, ) -> Result<(), CullingError> { // Compute the depth pyramid from last frame's depth buffer - self.compute_depth_pyramid - .run(device, queue, depth_texture)?; + self.compute_depth_pyramid.run(depth_texture)?; let (hzb_buffer, invalidate) = self .compute_depth_pyramid .depth_pyramid .slab - .get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )); + .get_updated_buffer_and_check(); if invalidate { self.invalidate_bindgroup(); } @@ -207,7 +203,7 @@ impl ComputeCulling { } pub struct DepthPyramid { - slab: SlabAllocator, + slab: SlabAllocator, desc: Hybrid, mip: GpuArray>, mip_data: Vec>, @@ -219,7 +215,7 @@ impl DepthPyramid { fn allocate( size: UVec2, desc: &Hybrid, - slab: &SlabAllocator, + slab: &SlabAllocator, ) -> (Vec>, GpuArray>) { let mip_levels = size.min_element().ilog2(); let mip_data = (0..mip_levels) @@ -239,8 +235,8 @@ impl DepthPyramid { (mip_data, mip.into_gpu_only()) } - pub fn new(size: UVec2) -> Self { - let slab = SlabAllocator::default(); + pub fn new(runtime: impl AsRef, size: UVec2) -> Self { + let slab = SlabAllocator::new_with_label(runtime, wgpu::BufferUsages::empty(), Self::LABEL); let desc = slab.new_value(DepthPyramidDescriptor::default()); let (mip_data, mip) = Self::allocate(size, &desc, &slab); @@ -252,12 +248,7 @@ impl DepthPyramid { } } - pub fn resize( - &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - size: UVec2, - ) -> (Arc, bool) { + pub fn resize(&mut self, size: UVec2) -> (Arc, bool) { log::info!("resizing depth pyramid to {size}"); let mip = self.slab.new_array(vec![]); self.mip_data = vec![]; @@ -265,12 +256,7 @@ impl DepthPyramid { self.mip = mip.into_gpu_only(); // Reclaim the dropped buffer slots - let (_, should_invalidate_a) = self.slab.get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )); + let (_, should_invalidate_a) = self.slab.get_updated_buffer_and_check(); // Reallocate let (mip_data, mip) = Self::allocate(size, &self.desc, &self.slab); @@ -278,12 +264,7 @@ impl DepthPyramid { self.mip = mip; // Run upkeep one more time to sync the resize - let (buffer, should_invalidate_b) = self.slab.get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )); + let (buffer, should_invalidate_b) = self.slab.get_updated_buffer_and_check(); (buffer, should_invalidate_a || should_invalidate_b) } @@ -291,12 +272,9 @@ impl DepthPyramid { self.desc.get().size } - pub async fn read_images( - &self, - ctx: &crate::Context, - ) -> Result, CullingError> { + pub async fn read_images(&self) -> Result, CullingError> { let size = self.size(); - let slab_data = self.slab.read(ctx, Self::LABEL, 0..).await?; + let slab_data = self.slab.read(0..).await?; let mut images = vec![]; let mut min = f32::MAX; let mut max = f32::MIN; @@ -446,23 +424,12 @@ impl ComputeCopyDepth { self.bindgroup = None; } - pub fn run( - &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - pyramid: &DepthPyramid, - depth_texture: &Texture, - ) { + pub fn run(&mut self, pyramid: &DepthPyramid, depth_texture: &Texture) { let size = pyramid.desc.modify(|desc| { desc.mip_level = 0; desc.size }); - let (slab_buffer, slab_buffer_is_new) = pyramid.slab.get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )); + let (slab_buffer, slab_buffer_is_new) = pyramid.slab.get_updated_buffer_and_check(); if slab_buffer_is_new { self.bindgroup = None; @@ -470,6 +437,7 @@ impl ComputeCopyDepth { let sample_count = depth_texture.texture.sample_count(); let sample_count_mismatch = sample_count != self.sample_count; + let device = pyramid.slab.device(); if sample_count_mismatch { log::info!( @@ -508,7 +476,7 @@ impl ComputeCopyDepth { let z = 1; compute_pass.dispatch_workgroups(x, y, z); } - queue.submit(Some(encoder.finish())); + pyramid.slab.queue().submit(Some(encoder.finish())); } } @@ -593,17 +561,13 @@ impl ComputeDownsampleDepth { self.bindgroup = None; } - pub fn run(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, pyramid: &DepthPyramid) { + pub fn run(&mut self, pyramid: &DepthPyramid) { + let device = pyramid.slab.device(); if self.bindgroup.is_none() { self.bindgroup = Some(Self::create_bindgroup( device, &self.bindgroup_layout, - &pyramid.slab.get_updated_buffer(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )), + &pyramid.slab.get_updated_buffer(), )); } for i in 1..pyramid.mip_data.len() { @@ -616,12 +580,7 @@ impl ComputeDownsampleDepth { desc.size }); // Sync the change. - let (_, should_invalidate) = pyramid.slab.get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )); + let (_, should_invalidate) = pyramid.slab.get_updated_buffer_and_check(); debug_assert!(!should_invalidate, "pyramid slab should never resize here"); let mut encoder = device @@ -640,7 +599,7 @@ impl ComputeDownsampleDepth { let z = 1; compute_pass.dispatch_workgroups(x, y, z); } - queue.submit(Some(encoder.finish())); + pyramid.slab.queue().submit(Some(encoder.finish())); } } } @@ -654,12 +613,13 @@ pub struct ComputeDepthPyramid { } impl ComputeDepthPyramid { - const LABEL: Option<&'static str> = Some("compute-depth-pyramid"); + const _LABEL: Option<&'static str> = Some("compute-depth-pyramid"); - pub fn new(device: &wgpu::Device, size: UVec2, sample_count: u32) -> Self { - let depth_pyramid = DepthPyramid::new(size); - let compute_copy_depth = ComputeCopyDepth::new(device, sample_count); - let compute_downsample_depth = ComputeDownsampleDepth::new(device); + pub fn new(runtime: impl AsRef, size: UVec2, sample_count: u32) -> Self { + let runtime = runtime.as_ref(); + let depth_pyramid = DepthPyramid::new(runtime, size); + let compute_copy_depth = ComputeCopyDepth::new(&runtime.device, sample_count); + let compute_downsample_depth = ComputeDownsampleDepth::new(&runtime.device); Self { depth_pyramid, compute_copy_depth, @@ -675,12 +635,7 @@ impl ComputeDepthPyramid { self.compute_copy_depth.invalidate(); } - pub fn run( - &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - depth_texture: &Texture, - ) -> Result<(), CullingError> { + pub fn run(&mut self, depth_texture: &Texture) -> Result<(), CullingError> { let sample_count = depth_texture.texture.sample_count(); if sample_count != self.sample_count { log::warn!( @@ -697,14 +652,9 @@ impl ComputeDepthPyramid { log::warn!("depth texture size changed, invalidating"); self.compute_copy_depth.invalidate(); self.compute_downsample_depth.invalidate(); - self.depth_pyramid.resize(device, queue, size) + self.depth_pyramid.resize(size) } else { - self.depth_pyramid.slab.get_updated_buffer_and_check(( - device, - queue, - Self::LABEL, - wgpu::BufferUsages::empty(), - )) + self.depth_pyramid.slab.get_updated_buffer_and_check() }; if should_invalidate { self.compute_copy_depth.invalidate(); @@ -712,10 +662,9 @@ impl ComputeDepthPyramid { } self.compute_copy_depth - .run(device, queue, &self.depth_pyramid, depth_texture); + .run(&self.depth_pyramid, depth_texture); - self.compute_downsample_depth - .run(device, queue, &self.depth_pyramid); + self.compute_downsample_depth.run(&self.depth_pyramid); Ok(()) } @@ -729,7 +678,7 @@ mod test { bvol::BoundingSphere, cull::DepthPyramidDescriptor, draw::DrawIndirectArgs, math::hex_to_vec4, prelude::*, }; - use crabslab::GrowableSlab; + use crabslab::{GrowableSlab, Slab}; use glam::{Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec4}; #[test] @@ -777,7 +726,7 @@ mod test { .drawing_strategy .as_indirect() .unwrap() - .read_hzb_images(&ctx), + .read_hzb_images(), ) .unwrap(); for (i, img) in pyramid_images.into_iter().enumerate() { @@ -1003,7 +952,7 @@ mod test { .drawing_strategy .as_indirect() .unwrap() - .read_hzb_images(&ctx), + .read_hzb_images(), ) .unwrap(); for (i, mut img) in pyramid_images.into_iter().enumerate() { @@ -1012,8 +961,7 @@ mod test { } // The stage's slab, which contains the `Renderlet`s and their `BoundingSphere`s - let stage_slab = - futures_lite::future::block_on(stage.read(&ctx, Some("read stage"), ..)).unwrap(); + let stage_slab = futures_lite::future::block_on(stage.read(..)).unwrap(); let draw_calls = stage.draw_calls.read().unwrap(); let indirect_draws = draw_calls.drawing_strategy.as_indirect().unwrap(); // The HZB slab, which contains a `DepthPyramidDescriptor` at index 0, and all the @@ -1024,13 +972,11 @@ mod test { .compute_depth_pyramid .depth_pyramid .slab - .read(&ctx, Some("read hzb desc"), ..), + .read(..), ) .unwrap(); // The indirect draw buffer - let mut args_slab = - futures_lite::future::block_on(indirect_draws.slab.read(&ctx, Some("read args"), ..)) - .unwrap(); + let mut args_slab = futures_lite::future::block_on(indirect_draws.slab.read(..)).unwrap(); let args: &mut [DrawIndirectArgs] = bytemuck::cast_slice_mut(&mut args_slab); // Number of `DrawIndirectArgs` in the `args` buffer. let num_draw_calls = draw_calls.draw_count(); diff --git a/crates/renderling/src/draw/cpu.rs b/crates/renderling/src/draw/cpu.rs index 4f131776..179a661c 100644 --- a/crates/renderling/src/draw/cpu.rs +++ b/crates/renderling/src/draw/cpu.rs @@ -2,13 +2,13 @@ use std::sync::Arc; +use craballoc::prelude::{Gpu, Hybrid, SlabAllocator, WeakHybrid, WgpuRuntime}; use crabslab::Id; use glam::UVec2; use rustc_hash::FxHashMap; use crate::{ cull::{ComputeCulling, CullingError}, - slab::{Gpu, Hybrid, SlabAllocator, WeakHybrid}, stage::Renderlet, texture::Texture, Context, @@ -42,16 +42,16 @@ impl InternalRenderlet { /// /// Issues draw calls and performs culling. pub struct IndirectDraws { - pub(crate) slab: SlabAllocator, + pub(crate) slab: SlabAllocator, pub(crate) draws: Vec>, pub(crate) compute_culling: ComputeCulling, } impl IndirectDraws { - fn new(device: &wgpu::Device, size: UVec2, sample_count: u32) -> Self { + fn new(runtime: impl AsRef, size: UVec2, sample_count: u32) -> Self { Self { - compute_culling: ComputeCulling::new(device, size, sample_count), - slab: SlabAllocator::default(), + compute_culling: ComputeCulling::new(runtime.as_ref(), size, sample_count), + slab: SlabAllocator::new(runtime, wgpu::BufferUsages::INDIRECT), draws: vec![], } } @@ -62,19 +62,12 @@ impl IndirectDraws { } } - fn get_indirect_buffer(&self, device: &wgpu::Device, queue: &wgpu::Queue) -> Arc { - self.slab.get_updated_buffer(( - device, - queue, - Some("indirect draw upkeep"), - wgpu::BufferUsages::INDIRECT, - )) + fn get_indirect_buffer(&self) -> Arc { + self.slab.get_updated_buffer() } fn sync_with_internal_renderlets( &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, internal_renderlets: &[InternalRenderlet], redraw_args: bool, ) -> Arc { @@ -82,12 +75,7 @@ impl IndirectDraws { self.invalidate(); // Pre-upkeep to reclaim resources - this is necessary because // the draw buffer has to be contiguous (it can't start with a bunch of trash) - if let Some(_indirect_buffer) = self.slab.upkeep(( - device, - queue, - Some("indirect draw pre upkeep"), - wgpu::BufferUsages::INDIRECT, - )) { + if let Some(_indirect_buffer) = self.slab.upkeep() { // Invalidate the compute culling buffer, it will be regenerated later... self.compute_culling.invalidate_bindgroup(); } @@ -100,21 +88,18 @@ impl IndirectDraws { }) .collect(); } - self.get_indirect_buffer(device, queue) + self.get_indirect_buffer() } /// Read the images from the hierarchical z-buffer used for occlusion /// culling. /// /// This is primarily for testing. - pub async fn read_hzb_images( - &self, - ctx: &crate::Context, - ) -> Result, CullingError> { + pub async fn read_hzb_images(&self) -> Result, CullingError> { self.compute_culling .compute_depth_pyramid .depth_pyramid - .read_images(ctx) + .read_images() .await } } @@ -183,11 +168,7 @@ impl DrawCalls { drawing_strategy: { if can_use_compute_culling { log::debug!("Using indirect drawing method and compute culling"); - DrawingStrategy::Indirect(IndirectDraws::new( - ctx.get_device(), - size, - sample_count, - )) + DrawingStrategy::Indirect(IndirectDraws::new(ctx, size, sample_count)) } else { log::debug!("Using direct drawing method"); DrawingStrategy::Direct @@ -273,7 +254,7 @@ impl DrawCalls { } /// Perform upkeep on queued draw calls and synchronize internal buffers. - pub fn upkeep(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + pub fn upkeep(&mut self) { let mut redraw_args = false; // Drop any renderlets that have no external references. @@ -288,12 +269,7 @@ impl DrawCalls { }); if let DrawingStrategy::Indirect(indirect) = &mut self.drawing_strategy { - indirect.sync_with_internal_renderlets( - device, - queue, - &self.internal_renderlets, - redraw_args, - ); + indirect.sync_with_internal_renderlets(&self.internal_renderlets, redraw_args); } } diff --git a/crates/renderling/src/lib.rs b/crates/renderling/src/lib.rs index 1ef4b474..a8129881 100644 --- a/crates/renderling/src/lib.rs +++ b/crates/renderling/src/lib.rs @@ -10,7 +10,7 @@ //! First you must create a [`crate::context::Context`]. //! //! ``` -//! use renderling::Context; +//! use renderling::prelude::*; //! //! // create a headless context with dimensions 100, 100. //! let ctx = Context::headless(100, 100); @@ -19,8 +19,7 @@ //! Then create a stage to place your camera, geometry, materials and lights. //! //! ``` -//! # use renderling::Context; -//! use renderling::stage::Stage; +//! # use renderling::prelude::*; //! # let ctx = Context::headless(100, 100); //! let stage: Stage = ctx //! .new_stage() @@ -32,15 +31,19 @@ //! The stage is neat in that it allows you to place values and arrays of values //! directly onto the GPU. Those values can be modified on the CPU and //! synchronization will happen during -//! [`Stage::render`](crate::stage::Stage::render). These values are called -//! [`Hybrid`](crate::slab::Hybrid)s and -//! [`HybridArray`](crate::slab::HybridArray)s. +//! [`Stage::render`](crate::stage::Stage::render). +//! +//! These values are called +//! [`Hybrid`](craballoc::Hybrid)s and +//! [`HybridArray`](craballoc::HybridArray)s. +//! +//! They come from the [`craballoc`] library, which is re-exported +//! from [the prelude](crate::prelude). //! //! ``` -//! # use renderling::{Context, stage::Stage}; +//! # use renderling::prelude::*; //! # let ctx = Context::headless(100, 100); //! # let stage: Stage = ctx.new_stage(); -//! use renderling::slab::{Hybrid, HybridArray}; //! //! let an_f32: Hybrid = stage.new_value(1337.0); //! @@ -57,13 +60,10 @@ //! counter-clockwise winding. //! //! ``` -//! # use renderling::{Context, stage::Stage}; +//! # use renderling::prelude::*; //! # let ctx = Context::headless(100, 100); //! # let stage: Stage = ctx.new_stage(); -//! use renderling::{ -//! camera::Camera, -//! stage::{Renderlet, Vertex}, -//! }; +//! //! let camera = stage.new_value(Camera::default_ortho2d(100.0, 100.0)); //! let vertices = stage.new_array([ //! Vertex::default() @@ -93,14 +93,7 @@ //! frame with [`Frame::present`]. //! //! ``` -//! # use renderling::{ -//! # Context, -//! # camera::Camera, -//! # stage::{ -//! # Vertex, -//! # Renderlet, -//! # } -//! # }; +//! # use renderling::prelude::*; //! # let ctx = Context::headless(100, 100); //! # let stage = ctx.new_stage(); //! # let camera = stage.new_value(Camera::default_ortho2d(100.0, 100.0)); @@ -166,7 +159,6 @@ pub mod math; pub mod pbr; pub mod sdf; pub mod skybox; -pub mod slab; pub mod stage; #[cfg(not(target_arch = "spirv"))] pub mod texture; @@ -181,16 +173,22 @@ pub use context::*; pub mod prelude { //! A prelude, meant to be glob-imported. + #[cfg(cpu)] + pub extern crate craballoc; pub extern crate glam; + + #[cfg(cpu)] + pub use craballoc::prelude::*; + pub use crabslab::{Array, Id}; + pub use crate::{ camera::*, pbr::{light::*, Material}, - slab::*, stage::*, transform::Transform, }; - #[cfg(not(target_arch = "spirv"))] + #[cfg(cpu)] pub use crate::context::*; } @@ -216,6 +214,7 @@ mod test { transform::Transform, }; + use craballoc::value::Hybrid; use glam::{Mat3, Mat4, Quat, UVec2, Vec2, Vec3, Vec4}; use img_diff::DiffCfg; use pretty_assertions::assert_eq; @@ -285,16 +284,12 @@ mod test { .hdr_texture .read() .unwrap() - .read_hdr_image(ctx.get_device(), ctx.get_queue()) + .read_hdr_image(&ctx) .unwrap(); //let hdr_img: RgbaImage = hdr_img.convert(); img_diff::save("cmy_triangle/hdr.png", hdr_img); - let bloom_mix = stage - .bloom - .get_mix_texture() - .read_hdr_image(ctx.get_device(), ctx.get_queue()) - .unwrap(); + let bloom_mix = stage.bloom.get_mix_texture().read_hdr_image(&ctx).unwrap(); img_diff::save("cmy_triangle/bloom_mix.png", bloom_mix); img_diff::assert_img_eq("cmy_triangle.png", img); @@ -556,7 +551,7 @@ mod test { ..Default::default() }); - let cube: slab::Hybrid = stage.new_value(Renderlet { + let cube: Hybrid = stage.new_value(Renderlet { camera_id: camera.id(), vertices_array: cube_geometry.array(), transform_id: transform.id(), diff --git a/crates/renderling/src/pbr.rs b/crates/renderling/src/pbr.rs index 0eca3f13..fea28e9c 100644 --- a/crates/renderling/src/pbr.rs +++ b/crates/renderling/src/pbr.rs @@ -762,9 +762,8 @@ mod test { } } - let (device, queue) = ctx.get_device_and_queue_owned(); let hdr_image = AtlasImage::from_hdr_path("../../img/hdr/resting_place.hdr").unwrap(); - let skybox = crate::skybox::Skybox::new(&device, &queue, hdr_image, camera.id()); + let skybox = crate::skybox::Skybox::new(&ctx, hdr_image, camera.id()); stage.set_skybox(skybox); let frame = ctx.get_next_frame().unwrap(); diff --git a/crates/renderling/src/skybox/cpu.rs b/crates/renderling/src/skybox/cpu.rs index 9542bfad..126e2540 100644 --- a/crates/renderling/src/skybox/cpu.rs +++ b/crates/renderling/src/skybox/cpu.rs @@ -1,12 +1,13 @@ //! CPU-side code for skybox rendering. +use craballoc::{ + prelude::{Hybrid, SlabAllocator}, + runtime::WgpuRuntime, +}; use crabslab::Id; use glam::{Mat4, UVec2, Vec3}; use crate::{ - atlas::AtlasImage, - camera::Camera, - convolution::VertexPrefilterEnvironmentCubemapIds, - slab::{Hybrid, SlabAllocator}, + atlas::AtlasImage, camera::Camera, convolution::VertexPrefilterEnvironmentCubemapIds, texture::Texture, }; @@ -169,7 +170,8 @@ pub struct Skybox { impl Skybox { /// Create an empty, transparent skybox. - pub fn empty(device: &wgpu::Device, queue: &wgpu::Queue) -> Self { + pub fn empty(runtime: impl AsRef) -> Self { + let runtime = runtime.as_ref(); log::trace!("creating empty skybox"); let hdr_img = AtlasImage { pixels: vec![0u8; 4 * 4], @@ -177,19 +179,19 @@ impl Skybox { format: crate::atlas::AtlasImageFormat::R32G32B32A32FLOAT, apply_linear_transfer: false, }; - Self::new(device, queue, hdr_img, crabslab::Id::::NONE) + Self::new(runtime, hdr_img, crabslab::Id::::NONE) } /// Create a new `Skybox`. pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, hdr_img: AtlasImage, - camera_id: crate::slab::Id, + camera_id: Id, ) -> Self { + let runtime = runtime.as_ref(); log::trace!("creating skybox"); - let slab = SlabAllocator::::default(); + let slab = SlabAllocator::new(runtime, wgpu::BufferUsages::VERTEX); let proj = Mat4::perspective_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1, 10.0); let camera = slab.new_value(Camera::default().with_projection(proj)); @@ -199,19 +201,13 @@ impl Skybox { roughness: roughness.id(), }); - let buffer = - slab.get_updated_buffer((device, queue, Some("skybox"), wgpu::BufferUsages::VERTEX)); + let buffer = slab.get_updated_buffer(); let mut buffer_upkeep = || { - let maybe_resized_buffer = slab.upkeep(( - device, - queue, - Some("skybox-upkeep"), - wgpu::BufferUsages::VERTEX, - )); + let maybe_resized_buffer = slab.upkeep(); debug_assert!(maybe_resized_buffer.is_none()); }; - let equirectangular_texture = Skybox::hdr_texture_from_atlas_image(device, queue, hdr_img); + let equirectangular_texture = Skybox::hdr_texture_from_atlas_image(runtime, hdr_img); let views = [ Mat4::look_at_rh( Vec3::new(0.0, 0.0, 0.0), @@ -247,8 +243,7 @@ impl Skybox { // Create environment map. let environment_cubemap = Skybox::create_environment_map_from_hdr( - device, - queue, + runtime, &buffer, &mut buffer_upkeep, &equirectangular_texture, @@ -258,8 +253,7 @@ impl Skybox { // Convolve the environment map. let irradiance_cubemap = Skybox::create_irradiance_map( - device, - queue, + runtime, &buffer, &mut buffer_upkeep, &environment_cubemap, @@ -269,8 +263,7 @@ impl Skybox { // Generate specular IBL pre-filtered environment map. let prefiltered_environment_cubemap = Skybox::create_prefiltered_environment_map( - device, - queue, + runtime, &buffer, &mut buffer_upkeep, &camera, @@ -280,7 +273,7 @@ impl Skybox { views, ); - let brdf_lut = Skybox::create_precomputed_brdf_texture(device, queue); + let brdf_lut = Skybox::create_precomputed_brdf_texture(runtime); Skybox { environment_cubemap, @@ -293,16 +286,15 @@ impl Skybox { /// Convert an HDR [`AtlasImage`] into a texture. pub fn hdr_texture_from_atlas_image( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, img: AtlasImage, ) -> Texture { + let runtime = runtime.as_ref(); Texture::new_with( - device, - queue, + runtime, Some("create hdr texture"), None, - Some(device.create_sampler(&wgpu::SamplerDescriptor { + Some(runtime.device.create_sampler(&wgpu::SamplerDescriptor { mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, @@ -319,24 +311,23 @@ impl Skybox { } /// Create an HDR equirectangular texture from bytes. - pub fn create_hdr_texture( - device: &wgpu::Device, - queue: &wgpu::Queue, - hdr_data: &[u8], - ) -> Texture { + pub fn create_hdr_texture(runtime: impl AsRef, hdr_data: &[u8]) -> Texture { + let runtime = runtime.as_ref(); let img = AtlasImage::from_hdr_bytes(hdr_data).unwrap(); - Self::hdr_texture_from_atlas_image(device, queue, img) + Self::hdr_texture_from_atlas_image(runtime, img) } fn create_environment_map_from_hdr( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, buffer: &wgpu::Buffer, buffer_upkeep: impl FnMut(), hdr_texture: &Texture, camera: &Hybrid, views: [Mat4; 6], ) -> Texture { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; // Create the cubemap-making pipeline. let pipeline = crate::cubemap::CubemapMakingRenderPipeline::new( device, @@ -353,8 +344,7 @@ impl Skybox { crate::cubemap::cubemap_making_bindgroup(device, resources.2, buffer, hdr_texture); Self::render_cubemap( - device, - queue, + runtime, &pipeline.0, buffer_upkeep, camera, @@ -367,8 +357,7 @@ impl Skybox { #[allow(clippy::too_many_arguments)] fn render_cubemap( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, pipeline: &wgpu::RenderPipeline, mut buffer_upkeep: impl FnMut(), camera: &Hybrid, @@ -377,6 +366,9 @@ impl Skybox { texture_size: u32, mip_levels: Option, ) -> Texture { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; let mut cubemap_faces = Vec::new(); let mip_levels = mip_levels.unwrap_or(1); @@ -387,8 +379,7 @@ impl Skybox { }); let mut cubemap_face = Texture::new_with( - device, - queue, + runtime, Some(&format!("cubemap{i}")), Some( wgpu::TextureUsages::RENDER_ATTACHMENT @@ -431,14 +422,13 @@ impl Skybox { } queue.submit([encoder.finish()]); - let mips = cubemap_face.generate_mips(device, queue, Some("cubemap mips"), mip_levels); + let mips = cubemap_face.generate_mips(runtime, Some("cubemap mips"), mip_levels); cubemap_faces.push(cubemap_face); cubemap_faces.extend(mips); } Texture::new_cubemap_texture( - device, - queue, + runtime, Some("skybox cubemap"), texture_size, cubemap_faces.as_slice(), @@ -448,14 +438,15 @@ impl Skybox { } fn create_irradiance_map( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, buffer: &wgpu::Buffer, buffer_upkeep: impl FnMut(), environment_texture: &Texture, camera: &Hybrid, views: [Mat4; 6], ) -> Texture { + let runtime = runtime.as_ref(); + let device = &runtime.device; let pipeline = crate::ibl::diffuse_irradiance::DiffuseIrradianceConvolutionRenderPipeline::new( device, @@ -470,8 +461,7 @@ impl Skybox { ); Self::render_cubemap( - device, - queue, + runtime, &pipeline.0, buffer_upkeep, camera, @@ -484,8 +474,7 @@ impl Skybox { #[allow(clippy::too_many_arguments)] fn create_prefiltered_environment_map( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, buffer: &wgpu::Buffer, mut buffer_upkeep: impl FnMut(), camera: &Hybrid, @@ -496,7 +485,7 @@ impl Skybox { ) -> Texture { let (pipeline, bindgroup) = crate::ibl::prefiltered_environment::create_pipeline_and_bindgroup( - device, + &runtime.as_ref().device, buffer, environment_texture, ); @@ -507,13 +496,14 @@ impl Skybox { let mip_width: u32 = 128 >> mip_level; let mip_height: u32 = 128 >> mip_level; - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("specular convolution"), - }); + let mut encoder = runtime.as_ref().device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { + label: Some("specular convolution"), + }, + ); let cubemap_face = Texture::new_with( - device, - queue, + runtime.as_ref(), Some(&format!("cubemap{i}{mip_level}prefiltered_environment")), Some(wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC), None, @@ -551,14 +541,13 @@ impl Skybox { render_pass.draw(0..36, prefilter_id.inner()..prefilter_id.inner() + 1); } - queue.submit([encoder.finish()]); + runtime.as_ref().queue.submit([encoder.finish()]); cubemap_faces.push(cubemap_face); } } Texture::new_cubemap_texture( - device, - queue, + runtime, Some("prefiltered environment cubemap"), 128, cubemap_faces.as_slice(), @@ -567,7 +556,10 @@ impl Skybox { ) } - fn create_precomputed_brdf_texture(device: &wgpu::Device, queue: &wgpu::Queue) -> Texture { + fn create_precomputed_brdf_texture(runtime: impl AsRef) -> Texture { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; let vertex_linkage = crate::linkage::brdf_lut_convolution_vertex::linkage(device); let fragment_linkage = crate::linkage::brdf_lut_convolution_fragment::linkage(device); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -612,8 +604,7 @@ impl Skybox { }); let framebuffer = Texture::new_with( - device, - queue, + runtime, Some("brdf_lut"), Some( wgpu::TextureUsages::RENDER_ATTACHMENT @@ -687,9 +678,8 @@ mod test { for i in 0..6 { // save out the irradiance face let copied_buffer = Texture::read_from( + &ctx, &skybox.irradiance_cubemap.texture, - ctx.get_device(), - ctx.get_queue(), 32, 32, 4, @@ -711,9 +701,8 @@ mod test { let mip_size = 128u32 >> mip_level; // save out the prefiltered environment faces' mips let copied_buffer = Texture::read_from( + &ctx, &skybox.prefiltered_environment_cubemap.texture, - ctx.get_device(), - ctx.get_queue(), mip_size as usize, mip_size as usize, 4, @@ -750,11 +739,10 @@ mod test { fn precomputed_brdf() { assert_eq!(2, std::mem::size_of::()); let r = Context::headless(32, 32); - let (device, queue) = r.get_device_and_queue_owned(); - let brdf_lut = Skybox::create_precomputed_brdf_texture(&device, &queue); + let brdf_lut = Skybox::create_precomputed_brdf_texture(&r); assert_eq!(wgpu::TextureFormat::Rg16Float, brdf_lut.texture.format()); - let copied_buffer = Texture::read(&brdf_lut.texture, &device, &queue, 512, 512, 2, 2); - let pixels = copied_buffer.pixels(&device); + let copied_buffer = Texture::read(&r, &brdf_lut.texture, 512, 512, 2, 2); + let pixels = copied_buffer.pixels(r.get_device()); let pixels: Vec = bytemuck::cast_slice::(pixels.as_slice()) .iter() .copied() diff --git a/crates/renderling/src/slab.rs b/crates/renderling/src/slab.rs deleted file mode 100644 index 5bdf9a1e..00000000 --- a/crates/renderling/src/slab.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! GPU and CPU slab allocation. -//! -//! Re-exports [`Array`], [`Id`], [`Slab`] and [`SlabItem`] from [`crabslab`](https://docs.rs/crabslab/latest/crabslab/). -//! -//! User types can automatically derive `SlabItem` in most cases. It is required -//! that your type's fields all implement `SlabItem` and `crabslab` must be in -//! scope. -//! -//! ``` -//! use renderling::slab::SlabItem; -//! -//! #[derive(Clone, Copy, SlabItem)] -//! struct UserData { -//! pos: (f32, f32, f32), -//! acc: (f32, f32, f32), -//! } -//! ``` -pub use crabslab::{Array, Id, Slab, SlabItem}; - -#[cfg(not(target_arch = "spirv"))] -mod cpu; -#[cfg(not(target_arch = "spirv"))] -pub use cpu::*; diff --git a/crates/renderling/src/slab/cpu.rs b/crates/renderling/src/slab/cpu.rs deleted file mode 100644 index ebb56e2f..00000000 --- a/crates/renderling/src/slab/cpu.rs +++ /dev/null @@ -1,1216 +0,0 @@ -//! Abstraction over a GPU buffer that provides near-automatic synchronization -//! and RAII memory management. -use core::{ - ops::Deref, - sync::atomic::{AtomicUsize, Ordering}, -}; -use crabslab::{Slab, SlabItem}; -use rustc_hash::{FxHashMap, FxHashSet}; -use snafu::prelude::*; -use std::sync::{atomic::AtomicBool, Arc, Mutex, RwLock, Weak}; - -pub use crabslab::{Array, Id}; - -#[derive(Clone, Copy, PartialEq)] -pub(crate) struct Range { - first_index: u32, - last_index: u32, -} - -impl core::fmt::Debug for Range { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str(&format!("{}..={}", self.first_index, self.last_index)) - } -} - -impl From> for Range { - fn from(array: Array) -> Self { - let array = array.into_u32_array(); - let first_index = array.starting_index() as u32; - Range { - first_index, - last_index: first_index + array.len() as u32 - 1, - } - } -} - -impl Range { - pub fn len(&self) -> u32 { - 1 + self.last_index - self.first_index - } - - pub fn intersects(&self, other: &Range) -> bool { - !(self.first_index > other.last_index || self.last_index < other.first_index) - } -} - -trait IsRange { - fn should_merge_with(&self, other: &Self) -> bool; - - fn union(&mut self, other: Self); -} - -impl IsRange for Range { - fn should_merge_with(&self, other: &Self) -> bool { - debug_assert!( - !self.intersects(other), - "{self:?} intersects existing {other:?}, should never happen with Range" - ); - - self.last_index + 1 == other.first_index || self.first_index == other.last_index + 1 - } - - fn union(&mut self, other: Self) { - *self = Range { - first_index: self.first_index.min(other.first_index), - last_index: self.last_index.max(other.last_index), - }; - } -} - -impl IsRange for SlabUpdate { - fn should_merge_with(&self, other: &Self) -> bool { - self.intersects(other) - } - - fn union(&mut self, other: Self) { - if self.array == other.array { - *self = other; - return; - } - - let mut array = self.array; - array.union(&other.array); - - let mut elements = vec![0u32; array.len()]; - - let self_index = self.array.index - array.index; - elements.write_indexed_slice(&self.elements, self_index as usize); - let other_index = other.array.index - array.index; - elements.write_indexed_slice(&other.elements, other_index as usize); - - self.array = array; - self.elements = elements; - } -} - -struct RangeManager { - ranges: Vec, -} - -impl Default for RangeManager { - fn default() -> Self { - Self { ranges: vec![] } - } -} - -impl RangeManager { - pub fn add_range(&mut self, input_range: R) { - for range in self.ranges.iter_mut() { - if range.should_merge_with(&input_range) { - range.union(input_range); - return; - } - } - self.ranges.push(input_range); - } -} - -impl RangeManager { - /// Removes a range of `count` elements, if possible. - pub fn remove(&mut self, count: u32) -> Option { - let mut remove_index = usize::MAX; - for (i, range) in self.ranges.iter_mut().enumerate() { - // This is potentially a hot path, so use the `if` even - // though clippy complains (because using match is slower) - #[allow(clippy::comparison_chain)] - if range.len() > count { - let first_index = range.first_index; - let last_index = range.first_index + count - 1; - range.first_index += count; - return Some(Range { - first_index, - last_index, - }); - } else if range.len() == count { - remove_index = i; - break; - } - } - - if remove_index == usize::MAX { - None - } else { - Some(self.ranges.swap_remove(remove_index)) - } - } -} - -#[derive(Debug, Snafu)] -pub enum SlabAllocatorError { - #[snafu(display( - "Slab has no internal buffer. Please call SlabAllocator::upkeep or \ - SlabAllocator::get_updated_buffer first." - ))] - NoInternalBuffer, - - #[snafu(display("Async recv error: {source}"))] - AsyncRecv { source: async_channel::RecvError }, - - #[snafu(display("Async error: {source}"))] - Async { source: wgpu::BufferAsyncError }, -} - -pub trait IsBuffer: Sized { - type Resources<'a>: Clone; - - /// Create a new buffer with the given capacity. - fn buffer_create(resources: Self::Resources<'_>, capacity: usize) -> Self; - - /// Copy the contents of one buffer into another at index 0. - fn buffer_copy(resources: Self::Resources<'_>, source_buffer: &Self, destination_buffer: &Self); - - /// Write updates to the buffer. - fn buffer_write>( - &self, - resources: Self::Resources<'_>, - updates: U, - ); -} - -impl IsBuffer for Mutex> { - type Resources<'a> = (); - - fn buffer_create((): Self::Resources<'_>, capacity: usize) -> Self { - log::trace!("creating vec with capacity {capacity}"); - Mutex::new(vec![0; capacity]) - } - - fn buffer_copy((): Self::Resources<'_>, source_buffer: &Self, destination_buffer: &Self) { - let source = source_buffer.lock().unwrap(); - let mut destination = destination_buffer.lock().unwrap(); - let destination_slice = &mut destination[0..source.len()]; - destination_slice.copy_from_slice(source.as_slice()); - } - - fn buffer_write>(&self, (): Self::Resources<'_>, updates: U) { - let mut guard = self.lock().unwrap(); - log::trace!("writing to vec len:{}", guard.len()); - for SlabUpdate { array, elements } in updates { - log::trace!("array: {array:?} elements: {elements:?}"); - let slice = &mut guard[array.starting_index()..array.starting_index() + array.len()]; - slice.copy_from_slice(&elements); - } - } -} - -/// Manages slab allocations and updates over a parameterised buffer. -/// -/// Create a new instance using [`SlabAllocator::default`]. -/// Upon creation you will need to call [`SlabAllocator::get_updated_buffer`] or -/// [`SlabAllocator::upkeep`] at least once before any data is written to the -/// internal buffer. -pub struct SlabAllocator { - pub(crate) notifier: (async_channel::Sender, async_channel::Receiver), - len: Arc, - capacity: Arc, - needs_expansion: Arc, - buffer: Arc>>>, - update_k: Arc, - update_sources: Arc>>, - update_queue: Arc>>, - recycles: Arc>>, -} - -impl Clone for SlabAllocator { - fn clone(&self) -> Self { - SlabAllocator { - notifier: self.notifier.clone(), - len: self.len.clone(), - capacity: self.capacity.clone(), - needs_expansion: self.needs_expansion.clone(), - buffer: self.buffer.clone(), - update_k: self.update_k.clone(), - update_sources: self.update_sources.clone(), - update_queue: self.update_queue.clone(), - recycles: self.recycles.clone(), - } - } -} - -impl Default for SlabAllocator { - fn default() -> Self { - Self { - notifier: async_channel::unbounded(), - update_k: Default::default(), - update_sources: Default::default(), - update_queue: Default::default(), - recycles: Default::default(), - len: Default::default(), - // Start with size 1, because some of `wgpu`'s validation depends on it. - // See for more info. - capacity: Arc::new(AtomicUsize::new(1)), - needs_expansion: Arc::new(true.into()), - buffer: Default::default(), - } - } -} - -impl IsBuffer for wgpu::Buffer { - type Resources<'a> = ( - &'a wgpu::Device, - &'a wgpu::Queue, - Option<&'a str>, - wgpu::BufferUsages, - ); - - fn buffer_write>( - &self, - (_, queue, _, _): Self::Resources<'_>, - updates: U, - ) { - for SlabUpdate { array, elements } in updates { - let offset = array.starting_index() as u64 * std::mem::size_of::() as u64; - queue.write_buffer(self, offset, bytemuck::cast_slice(&elements)); - } - queue.submit(std::iter::empty()); - } - - fn buffer_create((device, _, label, usages): Self::Resources<'_>, capacity: usize) -> Self { - let size = (capacity * std::mem::size_of::()) as u64; - let usage = usages - | wgpu::BufferUsages::STORAGE - | wgpu::BufferUsages::COPY_DST - | wgpu::BufferUsages::COPY_SRC; - device.create_buffer(&wgpu::BufferDescriptor { - label, - size, - usage, - mapped_at_creation: false, - }) - } - - fn buffer_copy( - (device, queue, label, _): Self::Resources<'_>, - source_buffer: &Self, - destination_buffer: &Self, - ) { - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label }); - encoder.copy_buffer_to_buffer( - source_buffer, - 0, - destination_buffer, - 0, - source_buffer.size(), - ); - queue.submit(std::iter::once(encoder.finish())); - } -} - -impl SlabAllocator { - pub(crate) fn next_update_k(&self) -> usize { - self.update_k.fetch_add(1, Ordering::Relaxed) - } - - pub(crate) fn insert_update_source(&self, k: usize, source: WeakGpuRef) { - log::trace!("slab insert_update_source {k}",); - let _ = self.notifier.0.try_send(k); - // UNWRAP: panic on purpose - self.update_sources.write().unwrap().insert(k, source); - } - - fn len(&self) -> usize { - self.len.load(Ordering::Relaxed) - } - - pub(crate) fn allocate(&self) -> Id { - // UNWRAP: we want to panic - let may_range = self.recycles.write().unwrap().remove(T::SLAB_SIZE as u32); - if let Some(range) = may_range { - let id = Id::::new(range.first_index); - log::trace!( - "slab allocate {}: dequeued {range:?} to {id:?}", - std::any::type_name::() - ); - debug_assert_eq!( - range.last_index, - range.first_index + T::SLAB_SIZE as u32 - 1 - ); - id - } else { - self.maybe_expand_to_fit::(1); - let index = self.increment_len(T::SLAB_SIZE); - Id::from(index) - } - } - - pub(crate) fn allocate_array(&self, len: usize) -> Array { - if len == 0 { - return Array::default(); - } - - // UNWRAP: we want to panic - let may_range = self - .recycles - .write() - .unwrap() - .remove((T::SLAB_SIZE * len) as u32); - if let Some(range) = may_range { - let array = Array::::new(range.first_index, len as u32); - log::trace!( - "slab allocate_array {len}x{}: dequeued {range:?} to {array:?}", - std::any::type_name::() - ); - debug_assert_eq!( - range.last_index, - range.first_index + (T::SLAB_SIZE * len) as u32 - 1 - ); - array - } else { - self.maybe_expand_to_fit::(len); - let index = self.increment_len(T::SLAB_SIZE * len); - Array::new(index as u32, len as u32) - } - } - - fn capacity(&self) -> usize { - self.capacity.load(Ordering::Relaxed) - } - - fn reserve_capacity(&self, capacity: usize) { - self.capacity.store(capacity, Ordering::Relaxed); - self.needs_expansion.store(true, Ordering::Relaxed); - } - - fn increment_len(&self, n: usize) -> usize { - self.len.fetch_add(n, Ordering::Relaxed) - } - - fn maybe_expand_to_fit(&self, len: usize) { - let capacity = self.capacity(); - // log::trace!( - // "append_slice: {size} * {ts_len} + {len} ({}) >= {capacity}", - // size * ts_len + len - //); - let capacity_needed = self.len() + T::SLAB_SIZE * len; - if capacity_needed > capacity { - let mut new_capacity = capacity * 2; - while new_capacity < capacity_needed { - new_capacity = (new_capacity * 2).max(2); - } - self.reserve_capacity(new_capacity); - } - } - - /// Return the internal buffer used by this slab. - /// - /// If the buffer needs recreating due to a capacity change this function - /// will return `None`. In that case use [`Self::get_updated_buffer`]. - pub fn get_buffer(&self) -> Option> { - self.buffer.read().unwrap().as_ref().cloned() - } - - /// Return an updated buffer. - /// - /// This is the only way to guarantee access to a buffer. - /// - /// Use [`SlabAllocator::upkeep`] when you only need the buffer after a - /// change, for example to recreate bindgroups. - pub fn get_updated_buffer(&self, resources: Buffer::Resources<'_>) -> Arc { - self.get_updated_buffer_and_check(resources).0 - } - - /// Return an updated buffer, and whether or not it is different from the - /// last one. - /// - /// This is the only way to guarantee access to a buffer. - /// - /// Use [`SlabAllocator::upkeep`] when you only need the buffer after a - /// change, for example to recreate bindgroups. - pub fn get_updated_buffer_and_check( - &self, - resources: Buffer::Resources<'_>, - ) -> (Arc, bool) { - if let Some(new_buffer) = self.upkeep(resources) { - (new_buffer, true) - } else { - // UNWRAP: safe because we know the buffer exists at this point, - // as we've called `upkeep` above - (self.get_buffer().unwrap(), false) - } - } - - /// Recreate this buffer, writing the contents of the previous buffer (if it - /// exists) to the new one, then return the new buffer. - fn recreate_buffer(&self, resources: Buffer::Resources<'_>) -> Arc { - let new_buffer = Arc::new(Buffer::buffer_create(resources.clone(), self.capacity())); - let mut guard = self.buffer.write().unwrap(); - if let Some(old_buffer) = guard.take() { - Buffer::buffer_copy(resources, &old_buffer, &new_buffer); - } - *guard = Some(new_buffer.clone()); - new_buffer - } - - /// Stage a new value that lives on the GPU _and_ CPU. - pub fn new_value(&self, value: T) -> Hybrid { - Hybrid::new(self, value) - } - - /// Stage a contiguous array of new values that live on the GPU _and_ CPU. - pub fn new_array( - &self, - values: impl IntoIterator, - ) -> HybridArray { - HybridArray::new(self, values) - } - - /// Return the ids of all sources that require updating. - pub fn get_updated_source_ids(&self) -> FxHashSet { - // UNWRAP: panic on purpose - let mut update_set = self.update_queue.write().unwrap(); - while let Ok(source_index) = self.notifier.1.try_recv() { - update_set.insert(source_index); - } - update_set.clone() - } - - /// Build the set of sources that require updates, draining the source - /// notifier and resetting the stored `update_queue`. - /// - /// This also places recycled items into the recycle bin. - fn drain_updated_sources(&self) -> RangeManager { - let update_set = self.get_updated_source_ids(); - // UNWRAP: panic on purpose - *self.update_queue.write().unwrap() = Default::default(); - // Prepare all of our GPU buffer writes - let mut writes = RangeManager::::default(); - { - // Recycle any update sources that are no longer needed, and collect the active - // sources' updates into `writes`. - let mut updates_guard = self.update_sources.write().unwrap(); - let mut recycles_guard = self.recycles.write().unwrap(); - for key in update_set { - let delete = if let Some(gpu_ref) = updates_guard.get_mut(&key) { - let count = gpu_ref.weak.strong_count(); - if count == 0 { - // recycle this allocation - let array = gpu_ref.u32_array; - log::debug!("slab drain_updated_sources: recycling {key} {array:?}"); - if array.is_null() { - log::debug!(" cannot recycle, null"); - } else if array.is_empty() { - log::debug!(" cannot recycle, empty"); - } else { - recycles_guard.add_range(gpu_ref.u32_array.into()); - } - true - } else { - gpu_ref - .get_update() - .into_iter() - .flatten() - .for_each(|u| writes.add_range(u)); - false - } - } else { - log::debug!("could not find {key}"); - false - }; - if delete { - let _ = updates_guard.remove(&key); - } - } - // Defrag the recycle ranges - let ranges = std::mem::take(&mut recycles_guard.ranges); - let num_ranges_to_defrag = ranges.len(); - for range in ranges.into_iter() { - recycles_guard.add_range(range); - } - let num_ranges = recycles_guard.ranges.len(); - if num_ranges < num_ranges_to_defrag { - log::trace!("{num_ranges_to_defrag} ranges before, {num_ranges} after"); - } - } - - writes - } - - /// Perform upkeep on the slab, commiting changes to the GPU. - /// - /// Returns the new buffer if one was created due to a capacity resize. - #[must_use] - pub fn upkeep(&self, resources: Buffer::Resources<'_>) -> Option> { - let new_buffer = if self.needs_expansion.swap(false, Ordering::Relaxed) { - Some(self.recreate_buffer(resources.clone())) - } else { - None - }; - - let writes = self.drain_updated_sources(); - if !writes.ranges.is_empty() { - // UNWRAP: safe because we know the buffer exists at this point, as we may have - // recreated it above^ - let buffer = self.get_buffer().unwrap(); - buffer.buffer_write(resources, writes.ranges.into_iter()); - } - new_buffer - } - - /// Defragments the internal "recycle" buffer. - pub fn defrag(&self) { - // UNWRAP: panic on purpose - let mut recycle_guard = self.recycles.write().unwrap(); - for range in std::mem::take(&mut recycle_guard.ranges) { - recycle_guard.add_range(range); - } - } -} - -impl SlabAllocator { - /// Read the range of data from the slab. - /// - /// This is primarily used for debugging. - pub async fn read( - &self, - ctx: &crate::Context, - label: Option<&str>, - range: impl std::ops::RangeBounds, - ) -> Result, SlabAllocatorError> { - let start = match range.start_bound() { - core::ops::Bound::Included(start) => *start, - core::ops::Bound::Excluded(start) => *start + 1, - core::ops::Bound::Unbounded => 0, - }; - let end = match range.end_bound() { - core::ops::Bound::Included(end) => *end + 1, - core::ops::Bound::Excluded(end) => *end, - core::ops::Bound::Unbounded => self.len(), - }; - let len = end - start; - let byte_offset = start * std::mem::size_of::(); - let length = len * std::mem::size_of::(); - let output_buffer_size = length as u64; - let output_buffer = ctx.get_device().create_buffer(&wgpu::BufferDescriptor { - label, - size: output_buffer_size, - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, - mapped_at_creation: false, - }); - let internal_buffer = self.get_buffer().context(NoInternalBufferSnafu)?; - - let mut encoder = ctx - .get_device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label }); - log::trace!( - "copy_buffer_to_buffer byte_offset:{byte_offset}, \ - output_buffer_size:{output_buffer_size}", - ); - encoder.copy_buffer_to_buffer( - &internal_buffer, - byte_offset as u64, - &output_buffer, - 0, - output_buffer_size, - ); - ctx.get_queue().submit(std::iter::once(encoder.finish())); - - let buffer_slice = output_buffer.slice(..); - let (tx, rx) = async_channel::bounded(1); - buffer_slice.map_async(wgpu::MapMode::Read, move |res| tx.try_send(res).unwrap()); - ctx.get_device().poll(wgpu::Maintain::Wait); - rx.recv() - .await - .context(AsyncRecvSnafu)? - .context(AsyncSnafu)?; - let bytes = buffer_slice.get_mapped_range(); - Ok(bytemuck::cast_slice(bytes.deref()).to_vec()) - } -} - -#[derive(Clone, Debug)] -pub struct SlabUpdate { - pub array: Array, - pub elements: Vec, -} - -impl SlabUpdate { - // pub fn range(&self) -> Range { - // Range { - // first_index: self.array.starting_index() as u32, - // last_index: (self.array.starting_index() + self.array.len()) as u32 - - // 1, } - // } - - pub fn intersects(&self, other: &Self) -> bool { - let here_start = self.array.index; - let there_start = other.array.index; - let here_end = self.array.index + self.array.len; - let there_end = other.array.index + other.array.len; - !(here_start >= there_end || there_start >= here_end) - } -} - -pub(crate) struct WeakGpuRef { - pub(crate) u32_array: Array, - pub(crate) weak: Weak>>, - pub(crate) takes_update: bool, -} - -impl WeakGpuRef { - fn get_update(&self) -> Option> { - let strong = self.weak.upgrade()?; - let mut guard = strong.lock().unwrap(); - let updates: Vec<_> = if self.takes_update { - std::mem::take(guard.as_mut()) - } else { - guard.clone() - }; - - if updates.is_empty() { - None - } else { - Some(updates) - } - } - - pub(crate) fn from_gpu(gpu: &Gpu) -> Self { - WeakGpuRef { - u32_array: Array::new(gpu.id.inner(), T::SLAB_SIZE as u32), - weak: Arc::downgrade(&gpu.update), - takes_update: true, - } - } - - pub(crate) fn from_gpu_array(gpu_array: &GpuArray) -> Self { - WeakGpuRef { - u32_array: gpu_array.array.into_u32_array(), - weak: Arc::downgrade(&gpu_array.updates), - takes_update: true, - } - } -} - -// impl UpdatesSlab for -// GpuArray { fn strong_count(&self) -> usize { -// Arc::strong_count(&self.updates) -// } - -// fn u32_array(&self) -> Array { -// self.array.into_u32_array() -// } - -// fn get_update(&self) -> Vec { -// std::mem::take(self.updates.lock().unwrap().as_mut()) -// } - -// fn id(&self) -> usize { -// self.notifier_index -// } -// } - -// impl UpdatesSlab for -// HybridArray { fn strong_count(&self) -> usize { -// self.gpu_value.strong_count() -// } - -// fn u32_array(&self) -> Array { -// self.gpu_value.u32_array() -// } - -// fn get_update(&self) -> Vec { -// self.gpu_value.get_update() -// } - -// fn id(&self) -> usize { -// self.gpu_value.notifier_index -// } -// } - -#[derive(Debug)] -pub struct WeakGpu { - pub(crate) id: Id, - pub(crate) notifier_index: usize, - pub(crate) notify: async_channel::Sender, - pub(crate) update: Weak>>, -} - -impl Clone for WeakGpu { - fn clone(&self) -> Self { - Self { - id: self.id, - notifier_index: self.notifier_index, - notify: self.notify.clone(), - update: self.update.clone(), - } - } -} - -impl WeakGpu { - pub fn from_gpu(gpu: &Gpu) -> Self { - Self { - id: gpu.id, - notifier_index: gpu.notifier_index, - notify: gpu.notify.clone(), - update: Arc::downgrade(&gpu.update), - } - } - - pub fn upgrade(&self) -> Option> { - Some(Gpu { - id: self.id, - notifier_index: self.notifier_index, - notify: self.notify.clone(), - update: self.update.upgrade()?, - }) - } -} - -#[derive(Debug)] -pub struct WeakHybrid { - pub(crate) weak_cpu: Weak>, - pub(crate) weak_gpu: WeakGpu, -} - -impl Clone for WeakHybrid { - fn clone(&self) -> Self { - Self { - weak_cpu: self.weak_cpu.clone(), - weak_gpu: self.weak_gpu.clone(), - } - } -} - -impl WeakHybrid { - pub fn id(&self) -> Id { - self.weak_gpu.id - } - - pub fn from_hybrid(h: &Hybrid) -> Self { - Self { - weak_cpu: Arc::downgrade(&h.cpu_value), - weak_gpu: WeakGpu::from_gpu(&h.gpu_value), - } - } - - pub fn upgrade(&self) -> Option> { - Some(Hybrid { - cpu_value: self.weak_cpu.upgrade()?, - gpu_value: self.weak_gpu.upgrade()?, - }) - } - - pub fn strong_count(&self) -> usize { - self.weak_gpu.update.strong_count() - } -} - -/// A "hybrid" type that lives on the CPU and the GPU. -/// -/// Updates are syncronized to the GPU once per frame. -/// -/// Clones of a hybrid all point to the same CPU and GPU data. -pub struct Hybrid { - pub(crate) cpu_value: Arc>, - pub(crate) gpu_value: Gpu, -} - -impl core::fmt::Debug for Hybrid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(&format!("Hybrid<{}>", std::any::type_name::())) - .field("id", &self.gpu_value.id) - .field("cpu_value", &self.cpu_value.read().unwrap()) - .finish() - } -} - -impl Clone for Hybrid { - fn clone(&self) -> Self { - Hybrid { - cpu_value: self.cpu_value.clone(), - gpu_value: self.gpu_value.clone(), - } - } -} - -impl Hybrid { - pub fn new(mngr: &SlabAllocator, value: T) -> Self { - let cpu_value = Arc::new(RwLock::new(value.clone())); - let gpu_value = Gpu::new(mngr, value); - Self { - cpu_value, - gpu_value, - } - } - - /// Returns the number of clones of this Hybrid on the CPU. - pub fn ref_count(&self) -> usize { - Arc::strong_count(&self.gpu_value.update) - } - - pub fn id(&self) -> Id { - self.gpu_value.id() - } - - pub fn get(&self) -> T { - self.cpu_value.read().unwrap().clone() - } - - pub fn modify(&self, f: impl FnOnce(&mut T) -> A) -> A { - let mut value_guard = self.cpu_value.write().unwrap(); - let a = f(&mut value_guard); - let t = value_guard.clone(); - self.gpu_value.set(t); - a - } - - pub fn set(&self, value: T) { - self.modify(move |old| { - *old = value; - }) - } - - /// Drop the CPU portion of the hybrid value, returning a type that wraps - /// only the GPU resources. - pub fn into_gpu_only(self) -> Gpu { - self.gpu_value - } -} - -/// A type that lives on the GPU. -/// -/// Updates are synchronized to the GPU during [`SlabAllocator::upkeep`]. -pub struct Gpu { - pub(crate) id: Id, - pub(crate) notifier_index: usize, - pub(crate) notify: async_channel::Sender, - pub(crate) update: Arc>>, -} - -impl Drop for Gpu { - fn drop(&mut self) { - let _ = self.notify.try_send(self.notifier_index); - } -} - -impl Clone for Gpu { - fn clone(&self) -> Self { - Self { - id: self.id, - notifier_index: self.notifier_index, - notify: self.notify.clone(), - update: self.update.clone(), - } - } -} - -impl Gpu { - pub fn new(mngr: &SlabAllocator, value: T) -> Self { - let id = mngr.allocate::(); - let notifier_index = mngr.next_update_k(); - let s = Self { - id, - notifier_index, - notify: mngr.notifier.0.clone(), - update: Default::default(), - }; - s.set(value); - mngr.insert_update_source(notifier_index, WeakGpuRef::from_gpu(&s)); - s - } - - pub fn id(&self) -> Id { - self.id - } - - pub fn set(&self, value: T) { - // UNWRAP: panic on purpose - *self.update.lock().unwrap() = vec![SlabUpdate { - array: Array::new(self.id.inner(), T::SLAB_SIZE as u32), - elements: { - let mut es = vec![0u32; T::SLAB_SIZE]; - es.write(Id::new(0), &value); - es - }, - }]; - // UNWRAP: safe because it's unbound - self.notify.try_send(self.notifier_index).unwrap(); - } -} - -/// A array type that lives on the GPU. -/// -/// Once created, the array cannot be resized. -/// -/// Updates are syncronized to the GPU once per frame. -#[derive(Debug)] -pub struct GpuArray { - array: Array, - notifier_index: usize, - notifier: async_channel::Sender, - updates: Arc>>, -} - -impl Drop for GpuArray { - fn drop(&mut self) { - let _ = self.notifier.try_send(self.notifier_index); - } -} - -impl Clone for GpuArray { - fn clone(&self) -> Self { - GpuArray { - notifier: self.notifier.clone(), - notifier_index: self.notifier_index, - array: self.array, - updates: self.updates.clone(), - } - } -} - -impl GpuArray { - pub fn new(mngr: &SlabAllocator, values: &[T]) -> Self { - let array = mngr.allocate_array::(values.len()); - let update = { - let mut elements = vec![0u32; T::SLAB_SIZE * array.len()]; - elements.write_indexed_slice(values, 0); - SlabUpdate { - array: array.into_u32_array(), - elements, - } - }; - let notifier_index = mngr.next_update_k(); - let g = GpuArray { - notifier_index, - notifier: mngr.notifier.0.clone(), - array, - updates: Arc::new(Mutex::new(vec![update])), - }; - mngr.insert_update_source(notifier_index, WeakGpuRef::from_gpu_array(&g)); - g - } - - pub fn len(&self) -> usize { - self.array.len() - } - - pub fn is_empty(&self) -> bool { - self.array.is_empty() - } - - pub fn array(&self) -> Array { - self.array - } - - pub fn get_id(&self, index: usize) -> Id { - self.array().at(index) - } - - pub fn set_item(&self, index: usize, value: &T) { - let id = self.array.at(index); - let array = Array::::new(id.inner(), T::SLAB_SIZE as u32); - let mut elements = vec![0u32; T::SLAB_SIZE]; - elements.write(0u32.into(), value); - self.updates - .lock() - .unwrap() - .push(SlabUpdate { array, elements }); - // UNWRAP: safe because it's unbounded - self.notifier.try_send(self.notifier_index).unwrap(); - } -} - -/// A "hybrid" array type that lives on the CPU and the GPU. -/// -/// Once created, the array cannot be resized. -/// -/// Updates are syncronized to the GPU once per frame. -pub struct HybridArray { - cpu_value: Arc>>, - gpu_value: GpuArray, -} - -impl core::fmt::Debug for HybridArray { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(&format!("HybridArray<{}>", std::any::type_name::())) - .field("array", &self.gpu_value.array) - .field("cpu_value", &self.cpu_value.read().unwrap()) - .finish() - } -} - -impl Clone for HybridArray { - fn clone(&self) -> Self { - HybridArray { - cpu_value: self.cpu_value.clone(), - gpu_value: self.gpu_value.clone(), - } - } -} - -impl HybridArray { - pub fn new(mngr: &SlabAllocator, values: impl IntoIterator) -> Self { - let values = values.into_iter().collect::>(); - let gpu_value = GpuArray::::new(mngr, &values); - let cpu_value = Arc::new(RwLock::new(values)); - HybridArray { - cpu_value, - gpu_value, - } - } - - pub fn ref_count(&self) -> usize { - Arc::strong_count(&self.gpu_value.updates) - } - - pub fn len(&self) -> usize { - self.gpu_value.array.len() - } - - pub fn is_empty(&self) -> bool { - self.gpu_value.is_empty() - } - - pub fn array(&self) -> Array { - self.gpu_value.array() - } - - pub fn get(&self, index: usize) -> Option { - self.cpu_value.read().unwrap().get(index).cloned() - } - - pub fn get_id(&self, index: usize) -> Id { - self.gpu_value.get_id(index) - } - - pub fn modify(&self, index: usize, f: impl FnOnce(&mut T) -> S) -> Option { - let mut value_guard = self.cpu_value.write().unwrap(); - let t = value_guard.get_mut(index)?; - let output = Some(f(t)); - self.gpu_value.set_item(index, t); - output - } - - pub fn set_item(&self, index: usize, value: T) -> Option { - self.modify(index, move |t| std::mem::replace(t, value)) - } - - pub fn into_gpu_only(self) -> GpuArray { - self.gpu_value - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn mngr_updates_count_sanity() { - let mngr = SlabAllocator::>>::default(); - { - let value = mngr.new_value(666u32); - assert_eq!( - 1, - value.ref_count(), - "slab should not retain a count on value" - ); - } - let _ = mngr.upkeep(()); - assert_eq!( - 0, - mngr.update_sources.read().unwrap().len(), - "value should have dropped with no refs" - ); - { - let values = mngr.new_array([666u32, 420u32]); - assert_eq!( - 1, - values.ref_count(), - "slab should not retain a count on array" - ); - } - let _ = mngr.upkeep(()); - assert_eq!( - 0, - mngr.update_sources.read().unwrap().len(), - "array should have dropped with no refs" - ); - } - - #[test] - fn range_sanity() { - let a = Range { - first_index: 1, - last_index: 2, - }; - let b = Range { - first_index: 0, - last_index: 0, - }; - assert!(!a.intersects(&b)); - assert!(!b.intersects(&a)); - } - - #[test] - fn slab_manager_sanity() { - let m = SlabAllocator::>>::default(); - log::info!("allocating 4 unused u32 slots"); - let _ = m.allocate::(); - let _ = m.allocate::(); - let _ = m.allocate::(); - let _ = m.allocate::(); - - log::info!("creating 4 update sources"); - let h4 = m.new_value(0u32); - let h5 = m.new_value(0u32); - let h6 = m.new_value(0u32); - let h7 = m.new_value(0u32); - log::info!("running upkeep"); - let _ = m.upkeep(()); - assert!(m.recycles.read().unwrap().ranges.is_empty()); - assert_eq!(4, m.update_sources.read().unwrap().len()); - let k = m.update_k.load(Ordering::Relaxed); - assert_eq!(4, k); - - log::info!("dropping 4 update sources"); - drop(h4); - drop(h5); - drop(h6); - drop(h7); - let _ = m.upkeep(()); - assert_eq!(1, m.recycles.read().unwrap().ranges.len()); - assert!(m.update_sources.read().unwrap().is_empty()); - - log::info!("creating 4 update sources, round two"); - let h4 = m.new_value(0u32); - let h5 = m.new_value(0u32); - let h6 = m.new_value(0u32); - let h7 = m.new_value(0u32); - assert!(m.recycles.read().unwrap().ranges.is_empty()); - assert_eq!(4, m.update_sources.read().unwrap().len()); - let k = m.update_k.load(Ordering::Relaxed); - // MAYBE_TODO: recycle "update_k"s instead of incrementing for each new source - assert_eq!(8, k); - - log::info!("creating one more update source, immediately dropping it and two others"); - let h8 = m.new_value(0u32); - drop(h8); - drop(h4); - drop(h6); - let _ = m.upkeep(()); - assert_eq!(3, m.recycles.read().unwrap().ranges.len()); - assert_eq!(2, m.update_sources.read().unwrap().len()); - assert_eq!(9, m.update_k.load(Ordering::Relaxed)); - - drop(h7); - drop(h5); - let _ = m.upkeep(()); - m.defrag(); - assert_eq!( - 1, - m.recycles.read().unwrap().ranges.len(), - "ranges: {:#?}", - m.recycles.read().unwrap().ranges - ); - } -} diff --git a/crates/renderling/src/stage.rs b/crates/renderling/src/stage.rs index 70cda871..91e741a7 100644 --- a/crates/renderling/src/stage.rs +++ b/crates/renderling/src/stage.rs @@ -522,13 +522,10 @@ pub fn test_atomic_i_add_sub( #[cfg(test)] mod test { - use std::sync::Mutex; - + use craballoc::{prelude::SlabAllocator, runtime::CpuRuntime}; use glam::{Mat4, Quat, Vec3}; - use crate::{ - math::IsMatrix, slab::SlabAllocator, stage::NestedTransform, transform::Transform, - }; + use crate::{math::IsMatrix, stage::NestedTransform, transform::Transform}; #[test] fn matrix_hierarchy_sanity() { @@ -565,7 +562,8 @@ mod test { (t, r, s) } - let slab = SlabAllocator::>>::default(); + #[expect(clippy::needless_borrows_for_generic_args, reason = "riffraff")] + let slab = SlabAllocator::::new(&CpuRuntime, ()); let a = NestedTransform::new(&slab); a.set(Transform { translation: Vec3::splat(100.0), diff --git a/crates/renderling/src/stage/cpu.rs b/crates/renderling/src/stage/cpu.rs index 9030a999..3535f6c7 100644 --- a/crates/renderling/src/stage/cpu.rs +++ b/crates/renderling/src/stage/cpu.rs @@ -3,6 +3,7 @@ //! The `Stage` object contains a slab buffer and a render pipeline. //! It is used to stage [`Renderlet`]s for rendering. use core::sync::atomic::{AtomicU32, Ordering}; +use craballoc::prelude::*; use crabslab::Id; use snafu::Snafu; use std::{ @@ -18,7 +19,6 @@ use crate::{ draw::DrawCalls, pbr::{debug::DebugChannel, light::Light, PbrConfig}, skybox::{Skybox, SkyboxRenderPipeline}, - slab::*, stage::Renderlet, texture::{DepthTexture, Texture}, tonemapping::Tonemapping, @@ -133,10 +133,7 @@ fn create_stage_render_pipeline( /// Only available on the CPU. Not available in shaders. #[derive(Clone)] pub struct Stage { - pub(crate) device: Arc, - pub(crate) queue: Arc, - - pub(crate) mngr: SlabAllocator, + pub(crate) mngr: SlabAllocator, pub(crate) pbr_config: Hybrid, pub(crate) lights: Arc>>>, @@ -170,7 +167,7 @@ pub struct Stage { } impl Deref for Stage { - type Target = SlabAllocator; + type Target = SlabAllocator; fn deref(&self) -> &Self::Target { &self.mngr @@ -180,11 +177,12 @@ impl Deref for Stage { impl Stage { /// Create a new stage. pub fn new(ctx: &crate::Context) -> Self { - let (device, queue) = ctx.get_device_and_queue_owned(); + let runtime = ctx.as_ref(); + let device = &runtime.device; let resolution @ UVec2 { x: w, y: h } = ctx.get_size(); let atlas_size = *ctx.atlas_size.read().unwrap(); - let atlas = Atlas::new(&device, &queue, atlas_size).unwrap(); - let mngr = SlabAllocator::default(); + let atlas = Atlas::new(ctx, atlas_size).unwrap(); + let mngr = SlabAllocator::new(runtime, wgpu::BufferUsages::empty()); let pbr_config = mngr.new_value(PbrConfig { atlas_size: UVec2::new(atlas_size.width, atlas_size.height), resolution, @@ -193,23 +191,22 @@ impl Stage { let multisample_count = 1; let lights = mngr.new_array(vec![Id::::NONE; 16]); let hdr_texture = Arc::new(RwLock::new(Texture::create_hdr_texture( - &device, + device, w, h, multisample_count, ))); let depth_texture = Arc::new(RwLock::new(Texture::create_depth_texture( - &device, + device, w, h, multisample_count, ))); let msaa_render_target = Default::default(); // UNWRAP: safe because no other references at this point (created above^) - let bloom = Bloom::new(&device, &queue, &hdr_texture.read().unwrap()); + let bloom = Bloom::new(ctx, &hdr_texture.read().unwrap()); let tonemapping = Tonemapping::new( - &device, - &queue, + runtime, ctx.get_render_target().format().add_srgb_suffix(), &bloom.get_mix_texture(), ); @@ -220,11 +217,11 @@ impl Stage { lights: Arc::new(RwLock::new(lights)), stage_pipeline: Arc::new(RwLock::new(create_stage_render_pipeline( - &device, + device, multisample_count, ))), atlas, - skybox: Arc::new(RwLock::new(Skybox::empty(&device, &queue))), + skybox: Arc::new(RwLock::new(Skybox::empty(runtime))), skybox_bindgroup: Default::default(), skybox_pipeline: Default::default(), has_skybox: Arc::new(AtomicBool::new(false)), @@ -239,7 +236,7 @@ impl Stage { UVec2::new(w, h), multisample_count, ))), - debug_overlay: DebugOverlay::new(&device, ctx.get_render_target().format()), + debug_overlay: DebugOverlay::new(device, ctx.get_render_target().format()), has_debug_overlay: Arc::new(false.into()), hdr_texture, depth_texture, @@ -248,15 +245,9 @@ impl Stage { clear_color_attachments: Arc::new(true.into()), clear_depth_attachments: Arc::new(true.into()), background_color: Arc::new(RwLock::new(wgpu::Color::TRANSPARENT)), - device, - queue, } } - pub fn get_device_and_queue_owned(&self) -> (Arc, Arc) { - (self.device.clone(), self.queue.clone()) - } - pub fn set_background_color(&self, color: impl Into) { let color = color.into(); *self.background_color.write().unwrap() = wgpu::Color { @@ -289,18 +280,18 @@ impl Stage { log::debug!("setting multisample count to {multisample_count}"); // UNWRAP: POP *self.stage_pipeline.write().unwrap() = - create_stage_render_pipeline(&self.device, multisample_count); + create_stage_render_pipeline(self.device(), multisample_count); let size = self.get_size(); // UNWRAP: POP *self.depth_texture.write().unwrap() = - Texture::create_depth_texture(&self.device, size.x, size.y, multisample_count); + Texture::create_depth_texture(self.device(), size.x, size.y, multisample_count); // UNWRAP: POP let format = self.hdr_texture.read().unwrap().texture.format(); *self.msaa_render_target.write().unwrap() = if multisample_count == 1 { None } else { Some(create_msaa_textureview( - &self.device, + self.device(), size.x, size.y, format, @@ -449,11 +440,11 @@ impl Stage { } self.pbr_config.modify(|cfg| cfg.resolution = size); - let hdr_texture = Texture::create_hdr_texture(&self.device, size.x, size.y, 1); + let hdr_texture = Texture::create_hdr_texture(self.device(), size.x, size.y, 1); let sample_count = self.msaa_sample_count.load(Ordering::Relaxed); if let Some(msaa_view) = self.msaa_render_target.write().unwrap().as_mut() { *msaa_view = create_msaa_textureview( - &self.device, + self.device(), size.x, size.y, hdr_texture.texture.format(), @@ -463,10 +454,10 @@ impl Stage { // UNWRAP: panic on purpose *self.depth_texture.write().unwrap() = - Texture::create_depth_texture(&self.device, size.x, size.y, sample_count); - self.bloom - .set_hdr_texture(&self.device, &self.queue, &hdr_texture); - self.tonemapping.set_hdr_texture(&self.device, &hdr_texture); + Texture::create_depth_texture(self.device(), size.x, size.y, sample_count); + self.bloom.set_hdr_texture(self.runtime(), &hdr_texture); + self.tonemapping + .set_hdr_texture(self.device(), &hdr_texture); *self.hdr_texture.write().unwrap() = hdr_texture; let _ = self.skybox_bindgroup.lock().unwrap().take(); @@ -483,7 +474,7 @@ impl Stage { /// This will cause a repacking. pub fn set_atlas_size(&self, size: wgpu::Extent3d) -> Result<(), StageError> { log::info!("resizing atlas to {size:?}"); - self.atlas.resize(&self.device, &self.queue, size)?; + self.atlas.resize(self.runtime(), size)?; Ok(()) } @@ -507,9 +498,7 @@ impl Stage { images: impl IntoIterator>, ) -> Result>, StageError> { let images = images.into_iter().map(|i| i.into()).collect::>(); - let frames = self - .atlas - .add_images(&self.device, &self.queue, self, &images)?; + let frames = self.atlas.add_images(self, &images)?; // The textures bindgroup will have to be remade let _ = self.textures_bindgroup.lock().unwrap().take(); @@ -539,9 +528,7 @@ impl Stage { images: impl IntoIterator>, ) -> Result>, StageError> { let images = images.into_iter().map(|i| i.into()).collect::>(); - let frames = self - .atlas - .set_images(&self.device, &self.queue, self, &images)?; + let frames = self.atlas.set_images(self, &images)?; // The textures bindgroup will have to be remade let _ = self.textures_bindgroup.lock().unwrap().take(); @@ -610,7 +597,7 @@ impl Stage { let pipeline = if let Some(pipeline) = pipeline_guard.as_mut() { if pipeline.msaa_sample_count() != msaa_sample_count { *pipeline = Arc::new(crate::skybox::create_skybox_render_pipeline( - &self.device, + self.device(), Texture::HDR_TEXTURE_FORMAT, Some(msaa_sample_count), )); @@ -618,7 +605,7 @@ impl Stage { pipeline.clone() } else { let pipeline = Arc::new(crate::skybox::create_skybox_render_pipeline( - &self.device, + self.device(), Texture::HDR_TEXTURE_FORMAT, Some(msaa_sample_count), )); @@ -631,7 +618,7 @@ impl Stage { bindgroup.clone() } else { let bg = Arc::new(crate::skybox::create_skybox_bindgroup( - &self.device, + self.device(), slab_buffer, &self.skybox.read().unwrap().environment_cubemap, )); @@ -648,7 +635,7 @@ impl Stage { bindgroup.clone() } else { let b = Arc::new({ - let device: &wgpu::Device = &self.device; + let device: &wgpu::Device = self.device(); crate::linkage::slab_bindgroup( device, slab_buffer, @@ -668,7 +655,7 @@ impl Stage { bindgroup.clone() } else { let b = Arc::new(crate::linkage::atlas_and_skybox_bindgroup( - &self.device, + self.device(), &{ let this = &self; this.stage_pipeline.clone() @@ -729,8 +716,7 @@ impl Stage { /// Returns a clone of the current depth texture. pub fn get_depth_texture(&self) -> DepthTexture { DepthTexture { - device: self.device.clone(), - queue: self.queue.clone(), + runtime: self.runtime().clone(), texture: self.depth_texture.read().unwrap().texture.clone(), } } @@ -741,7 +727,7 @@ impl Stage { camera_id: Id, ) -> Result { let hdr = AtlasImage::from_hdr_path(path)?; - Ok(Skybox::new(&self.device, &self.queue, hdr, camera_id)) + Ok(Skybox::new(self.runtime(), hdr, camera_id)) } pub fn new_nested_transform(&self) -> NestedTransform { @@ -754,17 +740,9 @@ impl Stage { } fn tick_internal(&self) -> Arc { - self.draw_calls - .write() - .unwrap() - .upkeep(&self.device, &self.queue); - - if let Some(new_slab_buffer) = self.mngr.upkeep(( - &self.device, - &self.queue, - Some("stage render upkeep"), - wgpu::BufferUsages::empty(), - )) { + self.draw_calls.write().unwrap().upkeep(); + + if let Some(new_slab_buffer) = self.mngr.upkeep() { // invalidate our bindgroups, etc let _ = self.skybox_bindgroup.lock().unwrap().take(); let _ = self.buffers_bindgroup.lock().unwrap().take(); @@ -782,7 +760,7 @@ impl Stage { /// It's good to call this after dropping assets to free up space on the /// slab. pub fn tick(&self) { - self.atlas.upkeep(&self.device, &self.queue); + self.atlas.upkeep(self.runtime()); let _ = self.tick_internal(); } @@ -792,7 +770,7 @@ impl Stage { let depth_texture = self.depth_texture.read().unwrap(); // UNWRAP: safe because we know the depth texture format will always match let maybe_indirect_buffer = draw_calls - .pre_draw(&self.device, &self.queue, &slab_buffer, &depth_texture) + .pre_draw(self.device(), self.queue(), &slab_buffer, &depth_texture) .unwrap(); { let label = Some("stage render"); @@ -814,7 +792,7 @@ impl Stage { let clear_depth = self.clear_depth_attachments.load(Ordering::Relaxed); let mut encoder = self - .device + .device() .create_command_encoder(&wgpu::CommandEncoderDescriptor { label }); { let hdr_texture = self.hdr_texture.read().unwrap(); @@ -871,28 +849,28 @@ impl Stage { render_pass.draw(0..36, skybox.camera.inner()..skybox.camera.inner() + 1); } } - self.queue.submit(std::iter::once(encoder.finish())); + self.queue().submit(std::iter::once(encoder.finish())); } // then render bloom if self.has_bloom.load(Ordering::Relaxed) { - self.bloom.bloom(&self.device, &self.queue); + self.bloom.bloom(self.device(), self.queue()); } else { // copy the input hdr texture to the bloom mix texture - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("no bloom copy"), - }); + let mut encoder = + self.device() + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("no bloom copy"), + }); let bloom_mix_texture = self.bloom.get_mix_texture(); encoder.copy_texture_to_texture( - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &self.hdr_texture.read().unwrap().texture, mip_level: 0, origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, aspect: wgpu::TextureAspect::All, }, - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &bloom_mix_texture.texture, mip_level: 0, origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, @@ -904,16 +882,16 @@ impl Stage { depth_or_array_layers: 1, }, ); - self.queue.submit(std::iter::once(encoder.finish())); + self.queue().submit(std::iter::once(encoder.finish())); } - self.tonemapping.render(&self.device, &self.queue, view); + self.tonemapping.render(self.device(), self.queue(), view); if self.has_debug_overlay.load(Ordering::Relaxed) { if let Some(indirect_draw_buffer) = maybe_indirect_buffer { self.debug_overlay.render( - &self.device, - &self.queue, + self.device(), + self.queue(), view, &slab_buffer, &indirect_draw_buffer, @@ -960,7 +938,7 @@ impl core::fmt::Debug for NestedTransform { } impl NestedTransform { - pub fn new(mngr: &SlabAllocator) -> Self { + pub fn new(mngr: &SlabAllocator) -> Self { let nested = NestedTransform { local_transform: Arc::new(RwLock::new(Transform::default())), global_transform: mngr.new_value(Transform::default()).into_gpu_only(), @@ -972,7 +950,7 @@ impl NestedTransform { } pub fn get_notifier_index(&self) -> usize { - self.global_transform.notifier_index + self.global_transform.notifier_index() } fn mark_dirty(&self) { @@ -1046,8 +1024,7 @@ impl NestedTransform { #[cfg(test)] mod test { - use std::sync::Mutex; - + use craballoc::runtime::CpuRuntime; use crabslab::{Array, Slab}; use glam::{Mat4, Vec2, Vec3}; @@ -1089,7 +1066,11 @@ mod test { #[test] fn can_global_transform_calculation() { - let slab = SlabAllocator::>>::default(); + #[expect( + clippy::needless_borrows_for_generic_args, + reason = "This is just riff-raff, as it doesn't compile without the borrow." + )] + let slab = SlabAllocator::::new(&CpuRuntime, ()); // Setup a hierarchy of transforms log::info!("new"); let root = NestedTransform::new(&slab); diff --git a/crates/renderling/src/stage/gltf_support.rs b/crates/renderling/src/stage/gltf_support.rs index 0de9bc3b..bbfee254 100644 --- a/crates/renderling/src/stage/gltf_support.rs +++ b/crates/renderling/src/stage/gltf_support.rs @@ -1,6 +1,7 @@ //! Gltf support for the [`Stage`](crate::Stage). use std::{collections::HashMap, sync::Arc}; +use craballoc::prelude::*; use crabslab::{Array, Id}; use glam::{Mat4, Quat, Vec2, Vec3, Vec4}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -13,7 +14,6 @@ use crate::{ light::{DirectionalLight, Light, LightStyle, PointLight, SpotLight}, Material, }, - slab::*, stage::{MorphTarget, NestedTransform, Renderlet, Skin, Stage, Vertex}, transform::Transform, }; @@ -596,8 +596,8 @@ pub struct GltfCamera { pub camera: Hybrid, } -impl<'a> GltfCamera { - fn new(stage: &mut Stage, gltf_camera: gltf::Camera<'a>, transform: &NestedTransform) -> Self { +impl GltfCamera { + fn new(stage: &mut Stage, gltf_camera: gltf::Camera<'_>, transform: &NestedTransform) -> Self { let projection = match gltf_camera.projection() { gltf::camera::Projection::Orthographic(o) => glam::Mat4::orthographic_rh( -o.xmag(), diff --git a/crates/renderling/src/stage/gltf_support/anime.rs b/crates/renderling/src/stage/gltf_support/anime.rs index cea805db..af80b706 100644 --- a/crates/renderling/src/stage/gltf_support/anime.rs +++ b/crates/renderling/src/stage/gltf_support/anime.rs @@ -1,11 +1,9 @@ //! Animation helpers for gltf. +use craballoc::prelude::HybridArray; use glam::{Quat, Vec3}; use snafu::prelude::*; -use crate::{ - slab::HybridArray, - stage::{gltf_support::GltfNode, NestedTransform}, -}; +use crate::stage::{gltf_support::GltfNode, NestedTransform}; #[derive(Debug, Snafu)] pub enum InterpolationError { diff --git a/crates/renderling/src/texture.rs b/crates/renderling/src/texture.rs index 5f2c10fc..597298fc 100644 --- a/crates/renderling/src/texture.rs +++ b/crates/renderling/src/texture.rs @@ -1,6 +1,7 @@ //! Wrapper around [`wgpu::Texture`]. use std::{ops::Deref, sync::Arc}; +use craballoc::runtime::WgpuRuntime; use glam::UVec2; use image::{ load_from_memory, DynamicImage, GenericImage, GenericImageView, ImageBuffer, ImageError, @@ -69,14 +70,14 @@ impl Texture { /// Create a cubemap texture from 6 faces. pub fn new_cubemap_texture( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, label: Option<&str>, texture_size: u32, face_textures: &[Texture], image_format: wgpu::TextureFormat, mip_levels: u32, ) -> Self { + let WgpuRuntime { device, queue } = runtime.as_ref(); let size = wgpu::Extent3d { width: texture_size, height: texture_size, @@ -105,13 +106,13 @@ impl Texture { let index = i * mip_levels as usize + mip_level; let texture = &face_textures[index].texture; encoder.copy_texture_to_texture( - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &cubemap_texture, mip_level: mip_level as u32, origin: wgpu::Origin3d { @@ -158,8 +159,7 @@ impl Texture { /// Create a new texture. #[allow(clippy::too_many_arguments)] pub fn new_with( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, label: Option<&str>, usage: Option, sampler: Option, @@ -171,6 +171,9 @@ impl Texture { mip_level_count: u32, data: &[u8], ) -> Self { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; let mip_level_count = 1.max(mip_level_count); let size = wgpu::Extent3d { width, @@ -192,14 +195,14 @@ impl Texture { if !data.is_empty() { queue.write_texture( - wgpu::ImageCopyTextureBase { + wgpu::TexelCopyTextureInfo { texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, data, - wgpu::ImageDataLayout { + wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(color_channels * color_channel_bytes * width), rows_per_image: None, @@ -234,8 +237,7 @@ impl Texture { /// byte per channel. #[allow(clippy::too_many_arguments)] pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, label: Option<&str>, usage: Option, color_channels: u32, @@ -243,9 +245,9 @@ impl Texture { height: u32, data: &[u8], ) -> Self { + let runtime = runtime.as_ref(); Self::new_with( - device, - queue, + runtime, label, usage, None, @@ -260,8 +262,7 @@ impl Texture { } pub fn from_image_bytes( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, bytes: &[u8], label: &str, ) -> Result { @@ -271,29 +272,31 @@ impl Texture { match img { DynamicImage::ImageLuma8(b) => { - Self::from_image_buffer(device, queue, &b, Some(label), None, None) + Self::from_image_buffer(runtime, &b, Some(label), None, None) } DynamicImage::ImageLumaA8(b) => { - Self::from_image_buffer(device, queue, &b, Some(label), None, None) + Self::from_image_buffer(runtime, &b, Some(label), None, None) } DynamicImage::ImageRgb8(b) => { - Self::from_image_buffer(device, queue, &b, Some(label), None, None) + Self::from_image_buffer(runtime, &b, Some(label), None, None) } DynamicImage::ImageRgba8(b) => { - Self::from_image_buffer(device, queue, &b, Some(label), None, None) + Self::from_image_buffer(runtime, &b, Some(label), None, None) } - img => Self::from_image_buffer(device, queue, &img.to_rgba8(), Some(label), None, None), + img => Self::from_image_buffer(runtime, &img.to_rgba8(), Some(label), None, None), } } pub fn from_dynamic_image( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, dyn_img: image::DynamicImage, label: Option<&str>, usage: Option, mip_level_count: u32, ) -> Self { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; let mip_level_count = mip_level_count.max(1); let dimensions = dyn_img.dimensions(); @@ -328,14 +331,14 @@ impl Texture { }); queue.write_texture( - wgpu::ImageCopyTextureBase { + wgpu::TexelCopyTextureInfo { texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, img.as_bytes(), - wgpu::ImageDataLayout { + wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(channels * dimensions.0), rows_per_image: Some(dimensions.1), @@ -347,8 +350,7 @@ impl Texture { } pub fn from_image_buffer

( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, img: &ImageBuffer>, label: Option<&str>, usage: Option, @@ -358,6 +360,7 @@ impl Texture { P: PixelWithColorType, ImageBuffer>: GenericImage + Deref, { + let runtime = runtime.as_ref(); let dimensions = img.dimensions(); let size = wgpu::Extent3d { @@ -366,7 +369,7 @@ impl Texture { depth_or_array_layers: 1, }; - let texture = device.create_texture(&wgpu::TextureDescriptor { + let texture = runtime.device.create_texture(&wgpu::TextureDescriptor { label, size, mip_level_count: 1, @@ -389,15 +392,15 @@ impl Texture { view_formats: &[], }); - queue.write_texture( - wgpu::ImageCopyTextureBase { + runtime.queue.write_texture( + wgpu::TexelCopyTextureInfo { texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, img.deref(), - wgpu::ImageDataLayout { + wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(P::CHANNEL_COUNT as u32 * dimensions.0), rows_per_image: Some(dimensions.1), @@ -405,7 +408,12 @@ impl Texture { size, ); - Ok(Self::from_wgpu_tex(device, texture, None, mip_level_count)) + Ok(Self::from_wgpu_tex( + &runtime.device, + texture, + None, + mip_level_count, + )) } pub fn from_wgpu_tex( @@ -416,14 +424,8 @@ impl Texture { ) -> Self { let texture = texture.into(); let view = Arc::new(texture.create_view(&wgpu::TextureViewDescriptor { - label: Some("texture view"), - format: None, - dimension: None, - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, mip_level_count, - base_array_layer: 0, - array_layer_count: None, + ..Default::default() })); let sampler_descriptor = sampler.unwrap_or_else(|| wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, @@ -496,19 +498,19 @@ impl Texture { /// To read the texture you must provide the width, height, the number of /// color/alpha channels and the number of bytes in the underlying /// subpixel type (usually u8=1, u16=2 or f32=4). + // TODO: remove width and height from these calls, as they can be obtained + // from Texture::size() pub fn read( + runtime: impl AsRef, texture: &wgpu::Texture, - device: &wgpu::Device, - queue: &wgpu::Queue, width: usize, height: usize, channels: usize, subpixel_bytes: usize, ) -> CopiedTextureBuffer { Self::read_from( + runtime, texture, - device, - queue, width, height, channels, @@ -525,9 +527,8 @@ impl Texture { /// subpixel type (usually u8=1, u16=2 or f32=4). #[allow(clippy::too_many_arguments)] pub fn read_from( + runtime: impl AsRef, texture: &wgpu::Texture, - device: &wgpu::Device, - queue: &wgpu::Queue, width: usize, height: usize, channels: usize, @@ -535,6 +536,9 @@ impl Texture { mip_level: u32, origin: Option, ) -> CopiedTextureBuffer { + let runtime = runtime.as_ref(); + let device = &runtime.device; + let queue = &runtime.queue; let dimensions = BufferDimensions::new(channels, subpixel_bytes, width, height); // The output buffer lets us retrieve the self as an array let buffer = device.create_buffer(&wgpu::BufferDescriptor { @@ -554,9 +558,9 @@ impl Texture { // Copy the data from the surface texture to the buffer encoder.copy_texture_to_buffer( source, - wgpu::ImageCopyBuffer { + wgpu::TexelCopyBufferInfo { buffer: &buffer, - layout: wgpu::ImageDataLayout { + layout: wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(dimensions.padded_bytes_per_row as u32), rows_per_image: None, @@ -579,22 +583,21 @@ impl Texture { pub fn read_hdr_image( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, ) -> Result { + let runtime = runtime.as_ref(); let width = self.width(); let height = self.height(); let copied = Texture::read( + runtime, &self.texture, - device, - queue, width as usize, height as usize, 4, 2, ); - let pixels = copied.pixels(device); + let pixels = copied.pixels(&runtime.device); let pixels = bytemuck::cast_slice::(pixels.as_slice()) .iter() .map(|p| half::f16::from_bits(*p).to_f32()) @@ -612,14 +615,14 @@ impl Texture { /// from an empty mip. pub fn generate_mips( &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, _label: Option<&str>, mip_levels: u32, ) -> Vec { - let generator = MipMapGenerator::new(device, self.texture.format()); + let runtime = runtime.as_ref(); + let generator = MipMapGenerator::new(&runtime.device, self.texture.format()); // UNWRAP: safe because we know the formats match. - generator.generate(device, queue, self, mip_levels).unwrap() + generator.generate(runtime, self, mip_levels).unwrap() } pub const HDR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float; @@ -672,14 +675,13 @@ impl Texture { } pub fn read_depth_texture_to_image( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, width: usize, height: usize, texture: &wgpu::Texture, ) -> Option { - let depth_copied_buffer = Texture::read(texture, device, queue, width, height, 1, 4); - let pixels = depth_copied_buffer.pixels(device); + let depth_copied_buffer = Texture::read(runtime.as_ref(), texture, width, height, 1, 4); + let pixels = depth_copied_buffer.pixels(&runtime.as_ref().device); let pixels = bytemuck::cast_slice::(&pixels) .iter() .copied() @@ -694,8 +696,7 @@ pub fn read_depth_texture_to_image( /// A depth texture. pub struct DepthTexture { - pub(crate) device: Arc, - pub(crate) queue: Arc, + pub(crate) runtime: WgpuRuntime, pub(crate) texture: Arc, } @@ -708,11 +709,9 @@ impl Deref for DepthTexture { } impl DepthTexture { - pub fn new(ctx: &crate::Context, texture: impl Into>) -> Self { - let (device, queue) = ctx.get_device_and_queue_owned(); + pub fn new(runtime: impl AsRef, texture: impl Into>) -> Self { Self { - device, - queue, + runtime: runtime.as_ref().clone(), texture: texture.into(), } } @@ -722,11 +721,12 @@ impl DepthTexture { /// Assumes the format is single channel 32bit. /// /// ## Panics - /// This may panic if the depth texture has a multisample count greater than 1. + /// This may panic if the depth texture has a multisample count greater than + /// 1. pub fn read_image(&self) -> Option { + // TODO: impl AsRef read_depth_texture_to_image( - &self.device, - &self.queue, + &self.runtime, self.width() as usize, self.height() as usize, &self.texture, @@ -953,14 +953,12 @@ mod test { #[test] fn generate_mipmaps() { let r = Context::headless(10, 10); - let (device, queue) = r.get_device_and_queue_owned(); let img = image::open("../../img/sandstone.png").unwrap(); let width = img.width(); let height = img.height(); let mip_level_count = 5; let mut texture = Texture::from_dynamic_image( - &device, - &queue, + &r, img, Some("sandstone"), Some( @@ -970,7 +968,7 @@ mod test { ), 1, ); - let mips = texture.generate_mips(&device, &queue, None, mip_level_count); + let mips = texture.generate_mips(&r, None, mip_level_count); let (channels, subpixel_bytes) = super::wgpu_texture_format_channels_and_subpixel_bytes(texture.texture.format()); @@ -980,9 +978,8 @@ mod test { let mip_height = height >> mip_level; // save out the mips let copied_buffer = Texture::read_from( + &r, &mip.texture, - r.get_device(), - r.get_queue(), mip_width as usize, mip_height as usize, channels as usize, diff --git a/crates/renderling/src/texture/mips.rs b/crates/renderling/src/texture/mips.rs index 5ab552d4..a327fb37 100644 --- a/crates/renderling/src/texture/mips.rs +++ b/crates/renderling/src/texture/mips.rs @@ -1,6 +1,7 @@ //! Mip-map generation. use crate::texture::Texture; +use craballoc::runtime::WgpuRuntime; use snafu::Snafu; use super::wgpu_texture_format_channels_and_subpixel_bytes; @@ -103,8 +104,7 @@ impl MipMapGenerator { /// Errors if the texture's format doesn't match the generator format. pub fn generate( &self, - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: impl AsRef, texture: &Texture, mip_levels: u32, ) -> Result, MipMapError> { @@ -127,8 +127,7 @@ impl MipMapGenerator { let mip_width = size.width >> mip_level; let mip_height = size.height >> mip_level; let mip_texture = Texture::new_with( - device, - queue, + runtime.as_ref(), Some(&format!("mip{mip_level}")), Some( wgpu::TextureUsages::COPY_SRC @@ -149,23 +148,28 @@ impl MipMapGenerator { } else { &mips[(mip_level - 2) as usize] }; - let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: LABEL, - layout: &self.bindgroup_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&prev_texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&prev_texture.sampler), - }, - ], - }); + let bindgroup = runtime + .as_ref() + .device + .create_bind_group(&wgpu::BindGroupDescriptor { + label: LABEL, + layout: &self.bindgroup_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&prev_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&prev_texture.sampler), + }, + ], + }); - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let mut encoder = runtime + .as_ref() + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -187,7 +191,10 @@ impl MipMapGenerator { render_pass.draw(0..6, 0..1); } - queue.submit(std::iter::once(encoder.finish())); + runtime + .as_ref() + .queue + .submit(std::iter::once(encoder.finish())); mips.push(mip_texture); } diff --git a/crates/renderling/src/tonemapping/cpu.rs b/crates/renderling/src/tonemapping/cpu.rs index fc25d5ae..22bc044f 100644 --- a/crates/renderling/src/tonemapping/cpu.rs +++ b/crates/renderling/src/tonemapping/cpu.rs @@ -1,11 +1,12 @@ //! Tonemapping. use core::ops::Deref; +use craballoc::{ + prelude::{Hybrid, SlabAllocator}, + runtime::WgpuRuntime, +}; use std::sync::{Arc, RwLock}; -use crate::{ - slab::{Hybrid, SlabAllocator}, - texture::Texture, -}; +use crate::texture::Texture; use super::TonemapConstants; @@ -86,7 +87,7 @@ pub fn create_bindgroup( /// Only available on CPU. Not Available in shaders. #[derive(Clone)] pub struct Tonemapping { - slab: SlabAllocator, + slab: SlabAllocator, config: Hybrid, hdr_texture: Arc>, bindgroup: Arc>, @@ -95,24 +96,23 @@ pub struct Tonemapping { impl Tonemapping { pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, + runtime: &WgpuRuntime, frame_texture_format: wgpu::TextureFormat, hdr_texture: &Texture, ) -> Self { - let slab = SlabAllocator::default(); + let slab = SlabAllocator::new(runtime, wgpu::BufferUsages::empty()); let config = slab.new_value(TonemapConstants::default()); let label = Some("tonemapping"); - let slab_buffer = - slab.get_updated_buffer((device, queue, label, wgpu::BufferUsages::empty())); + let slab_buffer = slab.get_updated_buffer(); let bindgroup = Arc::new(RwLock::new(create_bindgroup( - device, + &runtime.device, label, hdr_texture, &slab_buffer, ))); + let device = &runtime.device; let vertex_linkage = crate::linkage::tonemapping_vertex::linkage(device); let fragment_linkage = crate::linkage::tonemapping_fragment::linkage(device); let hdr_layout = bindgroup_layout(device, label); @@ -185,10 +185,7 @@ impl Tonemapping { pub fn render(&self, device: &wgpu::Device, queue: &wgpu::Queue, view: &wgpu::TextureView) { let label = Some("tonemapping render"); - assert!(self - .slab - .upkeep((device, queue, label, wgpu::BufferUsages::empty())) - .is_none()); + assert!(self.slab.upkeep().is_none()); // UNWRAP: not safe but we want to panic let bindgroup = self.bindgroup.read().unwrap(); diff --git a/crates/renderling/src/tutorial.rs b/crates/renderling/src/tutorial.rs index ba3498f2..c7bd2a8e 100644 --- a/crates/renderling/src/tutorial.rs +++ b/crates/renderling/src/tutorial.rs @@ -107,10 +107,11 @@ pub fn tutorial_slabbed_renderlet( #[cfg(test)] mod test { + use craballoc::prelude::{SlabAllocator, WgpuRuntime}; + use crate::{ camera::Camera, math::{Vec3, Vec4, Vec4Swizzles}, - slab::SlabAllocator, stage::{Renderlet, Vertex}, texture::Texture, transform::Transform, @@ -120,11 +121,11 @@ mod test { #[test] fn tutorial_implicit_isosceles_triangle() { let ctx = Context::headless(100, 100); - let (device, queue) = ctx.get_device_and_queue_owned(); + let WgpuRuntime { device, queue } = ctx.as_ref(); let label = Some("implicit isosceles triangle"); - let depth = Texture::create_depth_texture(&device, 100, 100, 1); - let vertex = crate::linkage::tutorial_implicit_isosceles_vertex::linkage(&device); - let fragment = crate::linkage::tutorial_passthru_fragment::linkage(&device); + let depth = Texture::create_depth_texture(device, 100, 100, 1); + let vertex = crate::linkage::tutorial_implicit_isosceles_vertex::linkage(device); + let fragment = crate::linkage::tutorial_passthru_fragment::linkage(device); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label, layout: None, @@ -207,10 +208,10 @@ mod test { #[test] fn slabbed_isosceles_triangle_no_instance() { let ctx = Context::headless(100, 100); - let (device, queue) = ctx.get_device_and_queue_owned(); + let WgpuRuntime { device, queue } = ctx.as_ref(); // Create our geometry on the slab. - let slab = SlabAllocator::::default(); + let slab = SlabAllocator::new(&ctx, wgpu::BufferUsages::empty()); let initial_vertices = [ Vertex { position: Vec3::new(0.5, -0.5, 0.0), @@ -252,8 +253,8 @@ mod test { push_constant_ranges: &[], }); - let vertex = crate::linkage::tutorial_slabbed_vertices_no_instance::linkage(&device); - let fragment = crate::linkage::tutorial_passthru_fragment::linkage(&device); + let vertex = crate::linkage::tutorial_slabbed_vertices_no_instance::linkage(device); + let fragment = crate::linkage::tutorial_passthru_fragment::linkage(device); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label, layout: Some(&pipeline_layout), @@ -298,8 +299,7 @@ mod test { cache: None, }); - let slab_buffer = - slab.get_updated_buffer((&device, &queue, None, wgpu::BufferUsages::empty())); + let slab_buffer = slab.get_updated_buffer(); let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { label, layout: &bindgroup_layout, @@ -309,7 +309,7 @@ mod test { }], }); - let depth = Texture::create_depth_texture(&device, 100, 100, 1); + let depth = Texture::create_depth_texture(device, 100, 100, 1); let frame = ctx.get_next_frame().unwrap(); let view = frame.view(); @@ -343,7 +343,7 @@ mod test { queue.submit(std::iter::once(encoder.finish())); // assert that we're reading the data correctly - let data = futures_lite::future::block_on(slab.read(&ctx, None, ..)).unwrap(); + let data = futures_lite::future::block_on(slab.read(..)).unwrap(); let mut vertices = vec![]; for i in 0..3 { let mut out_color = Vec4::ONE; @@ -366,10 +366,10 @@ mod test { #[test] fn tutorial_slabbed_isosceles_triangle() { let ctx = Context::headless(100, 100); - let (device, queue) = ctx.get_device_and_queue_owned(); + let WgpuRuntime { device, queue } = ctx.as_ref(); // Create our geometry on the slab. - let slab = SlabAllocator::::default(); + let slab = SlabAllocator::new(&ctx, wgpu::BufferUsages::empty()); let geometry = vec![ Vertex { position: Vec3::new(0.5, -0.5, 0.0), @@ -425,8 +425,8 @@ mod test { bind_group_layouts: &[&bindgroup_layout], push_constant_ranges: &[], }); - let vertex = crate::linkage::tutorial_slabbed_vertices::linkage(&device); - let fragment = crate::linkage::tutorial_passthru_fragment::linkage(&device); + let vertex = crate::linkage::tutorial_slabbed_vertices::linkage(device); + let fragment = crate::linkage::tutorial_passthru_fragment::linkage(device); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label, layout: Some(&pipeline_layout), @@ -471,8 +471,7 @@ mod test { cache: None, }); - let slab_buffer = - slab.get_updated_buffer((&device, &queue, None, wgpu::BufferUsages::empty())); + let slab_buffer = slab.get_updated_buffer(); let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { label, layout: &bindgroup_layout, @@ -482,7 +481,7 @@ mod test { }], }); - let depth = Texture::create_depth_texture(&device, 100, 100, 1); + let depth = Texture::create_depth_texture(device, 100, 100, 1); let frame = ctx.get_next_frame().unwrap(); let view = frame.view(); @@ -525,11 +524,11 @@ mod test { #[test] fn tutorial_slabbed_renderlet() { let ctx = Context::headless(100, 100); - let (device, queue) = ctx.get_device_and_queue_owned(); + let WgpuRuntime { device, queue } = ctx.as_ref(); // Create our geometry on the slab. // Don't worry too much about capacity, it can grow. - let slab = SlabAllocator::::default(); + let slab = SlabAllocator::new(&ctx, wgpu::BufferUsages::empty()); let geometry = slab.new_array([ Vertex { position: Vec3::new(0.5, -0.5, 0.0), @@ -597,8 +596,8 @@ mod test { push_constant_ranges: &[], }); - let vertex = crate::linkage::tutorial_slabbed_renderlet::linkage(&device); - let fragment = crate::linkage::tutorial_passthru_fragment::linkage(&device); + let vertex = crate::linkage::tutorial_slabbed_renderlet::linkage(device); + let fragment = crate::linkage::tutorial_passthru_fragment::linkage(device); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label, layout: Some(&pipeline_layout), @@ -643,8 +642,7 @@ mod test { cache: None, }); - let slab_buffer = - slab.get_updated_buffer((&device, &queue, None, wgpu::BufferUsages::empty())); + let slab_buffer = slab.get_updated_buffer(); let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { label, layout: &bindgroup_layout, @@ -654,7 +652,7 @@ mod test { }], }); - let depth = Texture::create_depth_texture(&device, 100, 100, 1); + let depth = Texture::create_depth_texture(device, 100, 100, 1); let frame = ctx.get_next_frame().unwrap(); let view = frame.view();