diff --git a/.cursor/mcp.json b/.cursor/mcp.json deleted file mode 100644 index 86d37b4..0000000 --- a/.cursor/mcp.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "mcpServers": { - "run-program": { - "command": "cargo", - "args": [ - "run" - ] - } - } -} diff --git a/Cargo.lock b/Cargo.lock index e69aec8..da7454f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "const-random", "getrandom", "once_cell", "version_check", @@ -40,6 +41,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -58,7 +65,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -117,48 +124,12 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "any_vec" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6ac04794a7749710e3c7f3c93222e3d04692993b69876d69393efd2565401a" - [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" -[[package]] -name = "apecs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6df7760d4baebb17003dcf99134d8e3a63f487e146d58911f0bcd27afb185d1c" -dependencies = [ - "any_vec", - "apecs-derive", - "async-channel", - "itertools", - "log", - "moongraph", - "parking_lot", - "rayon", - "smallvec", - "snafu 0.8.5", -] - -[[package]] -name = "apecs-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0f3ddfd31fd5276fb8039b75dc4d284c21213757a969e480c6ef8fde494f3b" -dependencies = [ - "moongraph-macros-syntax", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -187,16 +158,35 @@ dependencies = [ ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "assert_type_match" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", + "proc-macro2", + "quote", + "syn", ] +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -209,6 +199,127 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bevy_ecs" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1597106cc01e62e6217ccb662e0748b2ce330893f27c7dc17bac33e0bb99bca9" +dependencies = [ + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bitflags 2.9.0", + "concurrent-queue", + "derive_more", + "disqualified", + "fixedbitset 0.5.7", + "nonmax", + "petgraph", + "smallvec", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f453adf07712b39826bc5845e5b0887ce03204ee8359bbe6b40a9afda60564a1" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb6ded1ddc124ea214f6a2140e47a78d1fe79b0638dad39419cdeef2e1133f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_ptr" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89fe0b0b919146939481a3a7c38864face2c6d0fd2c73ab3d430dc693ecd9b11" + +[[package]] +name = "bevy_reflect" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddbca0a39e88eff2c301dc794ee9d73a53f4b08d47b2c9b5a6aac182fae6217" +dependencies = [ + "assert_type_match", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "serde", + "smallvec", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d62affb769db17d34ad0b75ff27eca94867e2acc8ea350c5eca97d102bd98709" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_tasks" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028630ddc355563bd567df1076db3515858aa26715ddf7467d2086f9b40e5ab1" +dependencies = [ + "async-executor", + "futures-channel", + "futures-lite", + "pin-project", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_utils" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63c2174d43a0de99f863c98a472370047a2bfa7d1e5cec8d9d647fb500905d9d" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown 0.14.5", + "thread_local", + "tracing", + "web-time", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94847541f6dd2e28f54a9c2b0e857da5f2631e2201ebc25ce68781cdcb721391" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -230,17 +341,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "broomdog" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ec65645d8167b03c07e049f114a878a11ab889f20c071d6f7b30bf88fbe5af" -dependencies = [ - "log", - "rustc-hash", - "snafu 0.8.5", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -264,7 +364,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -284,7 +384,7 @@ dependencies = [ "polling", "rustix", "slab", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -362,6 +462,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -402,25 +522,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -449,14 +550,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] -name = "dagga" -version = "0.2.1" +name = "derive_more" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf0d7dcd307c9c5d81277737c35d1faf08af9e2cb262966a01c91021686b68" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "log", - "rustc-hash", - "snafu 0.7.5", + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] @@ -465,6 +576,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + [[package]] name = "dlib" version = "0.5.2" @@ -474,12 +591,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "downcast-rs" version = "1.2.1" @@ -492,12 +603,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "env_filter" version = "0.1.3" @@ -527,6 +632,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.10" @@ -538,10 +653,22 @@ dependencies = [ ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" @@ -567,7 +694,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -576,12 +703,40 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -599,8 +754,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -620,6 +777,17 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -645,7 +813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] @@ -654,15 +822,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -690,7 +849,7 @@ checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -704,7 +863,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -804,30 +963,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "moongraph" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a4b09eb96a84205062b48ec5469c8c35c128167e838aa73dc620c4411af598" -dependencies = [ - "broomdog", - "dagga", - "log", - "rayon", - "snafu 0.8.5", -] - -[[package]] -name = "moongraph-macros-syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08112087acc92cc28fb5d8f7bda1307123ecc9a275ed4835f1c03f1a8dd02c1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ndk" version = "0.9.0" @@ -840,7 +975,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -868,6 +1003,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + [[package]] name = "num_enum" version = "0.7.3" @@ -886,7 +1027,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1170,6 +1311,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1199,6 +1346,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -1216,7 +1373,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1315,26 +1472,6 @@ dependencies = [ "objc2-quartz-core 0.3.0", ] -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1396,21 +1533,16 @@ name = "rust_vulkan_test" version = "0.1.0" dependencies = [ "anyhow", - "apecs", + "bevy_ecs", "env_logger", "glam", "log", + "thiserror 2.0.12", "vulkano", "vulkano-shaders", "winit", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustix" version = "0.38.44" @@ -1487,7 +1619,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1564,7 +1696,7 @@ dependencies = [ "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -1584,66 +1716,12 @@ dependencies = [ "serde", ] -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "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]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "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", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.100" @@ -1661,7 +1739,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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1672,7 +1759,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1685,6 +1783,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -1742,6 +1849,9 @@ name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] [[package]] name = "ttf-parser" @@ -1749,6 +1859,12 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -1761,12 +1877,27 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +dependencies = [ + "getrandom", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1823,7 +1954,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] [[package]] @@ -1837,7 +1968,7 @@ dependencies = [ "proc-macro2", "quote", "shaderc", - "syn 2.0.100", + "syn", "vulkano", ] @@ -1879,7 +2010,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-shared", ] @@ -1914,7 +2045,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2418,5 +2549,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index ba7eb9d..a84f681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] anyhow = "1.0" +thiserror = "2.0" winit = { version = "0.30", features = ["rwh_06"] } vulkano = "0.35" @@ -16,7 +17,7 @@ vulkano-shaders = "0.35" glam = { version = "0.30" } # ECS -apecs = "0.8" +bevy_ecs = "0.15.3" # Log and tracing log = "0.4" diff --git a/flake.nix b/flake.nix index 565976b..4f7cd2e 100644 --- a/flake.nix +++ b/flake.nix @@ -39,6 +39,8 @@ buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [ + stdenv.cc.cc.lib + # Wayland libxkbcommon wayland diff --git a/src/core/app/app.rs b/src/core/app/app.rs new file mode 100644 index 0000000..3821bfc --- /dev/null +++ b/src/core/app/app.rs @@ -0,0 +1,56 @@ +use std::error::Error; + +use bevy_ecs::world::World; + +pub enum AppExit { + Success, + Error(Box), +} + +pub type RunnerFn = Box AppExit>; + +#[derive(Debug, thiserror::Error)] +pub enum AppError { + #[error("Runner is not set")] + RunnerNotSet, + #[error("Runner returned an error : {0}")] + RunnerError(Box), +} + +pub struct App { + world: World, + runner: Option, +} + +impl Default for App { + fn default() -> Self { + Self { + world: World::new(), + runner: None, + } + } +} + +impl App { + pub fn world_mut(&mut self) -> &mut World { + &mut self.world + } + + pub fn world(&self) -> &World { + &self.world + } + + pub fn run(mut self) -> Result<(), AppError> { + match self.runner.take() { + Some(runner) => match runner(self) { + AppExit::Success => Ok(()), + AppExit::Error(e) => Err(AppError::RunnerError(e)), + }, + None => Err(AppError::RunnerNotSet), + } + } + + pub fn set_runner(&mut self, runner: RunnerFn) { + self.runner = Some(runner); + } +} diff --git a/src/core/app/mod.rs b/src/core/app/mod.rs new file mode 100644 index 0000000..c6c8a20 --- /dev/null +++ b/src/core/app/mod.rs @@ -0,0 +1,2 @@ +mod app; +pub use app::*; diff --git a/src/core/camera/mod.rs b/src/core/camera/mod.rs new file mode 100644 index 0000000..f9b6925 --- /dev/null +++ b/src/core/camera/mod.rs @@ -0,0 +1,19 @@ +use bevy_ecs::component::Component; +use glam::{Mat4, Quat, Vec3}; + +pub trait Camera: Into + Component {} + +#[derive(Component)] +pub struct Camera3D { + pub projection: Mat4, + pub position: Vec3, + pub rotation: Quat, +} + +impl Into for Camera3D { + fn into(self) -> Mat4 { + Mat4::from_rotation_translation(self.rotation, self.position) * self.projection + } +} + +impl Camera for Camera3D {} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..abaeeae --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,5 @@ +pub mod app; +pub mod camera; +pub mod render; +pub mod vulkan; +pub mod window; diff --git a/src/core/render/material.rs b/src/core/render/material.rs new file mode 100644 index 0000000..539d4bc --- /dev/null +++ b/src/core/render/material.rs @@ -0,0 +1,7 @@ +use std::sync::Arc; + +use bevy_ecs::component::Component; +use vulkano::pipeline::GraphicsPipeline; + +#[derive(Component)] +pub struct Material(pub Arc); diff --git a/src/core/render/mesh.rs b/src/core/render/mesh.rs new file mode 100644 index 0000000..ca5ec0f --- /dev/null +++ b/src/core/render/mesh.rs @@ -0,0 +1,14 @@ +use bevy_ecs::component::Component; + +use super::vertex::Vertex2D; + +#[derive(Component)] +pub struct Mesh2D { + pub vertices: Vec, +} + +impl Mesh2D { + pub fn new(vertices: Vec) -> Self { + Self { vertices } + } +} diff --git a/src/core/render/mod.rs b/src/core/render/mod.rs new file mode 100644 index 0000000..5d003a8 --- /dev/null +++ b/src/core/render/mod.rs @@ -0,0 +1,3 @@ +pub mod material; +pub mod mesh; +pub mod vertex; diff --git a/src/vulkan/resources/vertex.rs b/src/core/render/vertex.rs similarity index 82% rename from src/vulkan/resources/vertex.rs rename to src/core/render/vertex.rs index cc25b1b..9bf133e 100644 --- a/src/vulkan/resources/vertex.rs +++ b/src/core/render/vertex.rs @@ -6,7 +6,7 @@ use vulkano::buffer::{ use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; use vulkano::pipeline::graphics::vertex_input::Vertex; -#[derive(BufferContents, Vertex, Clone)] +#[derive(BufferContents, Vertex)] #[repr(C)] pub struct Vertex2D { #[format(R32G32_SFLOAT)] @@ -16,14 +16,6 @@ pub struct Vertex2D { pub color: [f32; 3], } -#[derive(BufferContents, Clone)] -#[repr(C)] -pub struct MVPData { - pub world: [[f32; 4]; 4], - pub view: [[f32; 4]; 4], - pub projection: [[f32; 4]; 4], -} - impl Vertex2D { pub fn create_buffer( vertices: Vec, @@ -40,7 +32,7 @@ impl Vertex2D { | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, ..Default::default() }, - vertices.into_iter(), + vertices, ) } } diff --git a/src/core/vulkan/mod.rs b/src/core/vulkan/mod.rs new file mode 100644 index 0000000..fe22fb3 --- /dev/null +++ b/src/core/vulkan/mod.rs @@ -0,0 +1,28 @@ +use vulkan_context::VulkanContext; +use window_render_context::WindowRenderContext; + +use super::app::App; + +mod utils; +mod vulkan_context; +mod window_render_context; + +#[derive(Debug, thiserror::Error)] +pub enum VulkanError { + #[error("Failed to create vulkan context")] + FailedToCreateVulkanContext, +} + +pub struct Vulkan; + +impl Vulkan { + pub fn new(app: &mut App) -> Result<(), VulkanError> { + let vulkan_context = VulkanContext::from(app as &App); + app.world_mut().insert_resource(vulkan_context); + + let window_render_context = WindowRenderContext::from(app as &App); + app.world_mut().insert_resource(window_render_context); + + Ok(()) + } +} diff --git a/src/core/vulkan/utils.rs b/src/core/vulkan/utils.rs new file mode 100644 index 0000000..046528f --- /dev/null +++ b/src/core/vulkan/utils.rs @@ -0,0 +1,137 @@ +use std::sync::Arc; + +use vulkano::{ + Version, VulkanLibrary, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, + QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, +}; +use winit::raw_window_handle::HasDisplayHandle; + +pub(super) fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available layer:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +pub(super) fn create_instance( + library: Arc, + required_extensions: InstanceExtensions, +) -> Arc { + Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + ..Default::default() + }, + ) + .unwrap() +} + +pub(super) fn find_physical_device_queue_family_indexes( + physical_device: &Arc, + display_handle: &impl HasDisplayHandle, +) -> Option { + let mut graphic_queue_family_index = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS) + && physical_device + .presentation_support(i as u32, display_handle) + .unwrap() + { + graphic_queue_family_index = Some(i as u32); + } + } + + graphic_queue_family_index +} + +pub(super) fn pick_physical_device_and_queue_family_indexes( + instance: &Arc, + display_handle: &impl HasDisplayHandle, + device_extensions: &DeviceExtensions, +) -> Option<(Arc, u32)> { + instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| p.supported_extensions().contains(device_extensions)) + .filter_map(|p| { + find_physical_device_queue_family_indexes(&p, display_handle) + .and_then(|indexes| Some((p, indexes))) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) +} + +pub(super) fn pick_graphics_device( + instance: &Arc, + display_handle: &impl HasDisplayHandle, +) -> (Arc, impl ExactSizeIterator>) { + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, graphics_family_index) = + pick_physical_device_and_queue_family_indexes(instance, display_handle, &device_extensions) + .unwrap(); + + log::debug!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + log::debug!("Using device extensions: {:#?}", device_extensions); + + Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index: graphics_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap() +} diff --git a/src/core/vulkan/vulkan_context.rs b/src/core/vulkan/vulkan_context.rs new file mode 100644 index 0000000..f59d9a3 --- /dev/null +++ b/src/core/vulkan/vulkan_context.rs @@ -0,0 +1,87 @@ +use std::{any::Any, sync::Arc}; + +use bevy_ecs::system::Resource; +use vulkano::{ + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{Device, Queue}, + instance::Instance, + memory::allocator::StandardMemoryAllocator, + swapchain::Surface, +}; +use winit::raw_window_handle::{HasDisplayHandle, HasWindowHandle}; + +use crate::core::{app::App, window::raw_handle::DisplayHandleWrapper}; + +use super::utils; + +#[derive(Resource)] +pub struct VulkanContext { + pub instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl VulkanContext { + pub fn create_surface( + &self, + window: Arc, + ) -> Arc { + Surface::from_window(self.instance.clone(), window).unwrap() + } + + pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { + AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap() + } +} + +impl From<&App> for VulkanContext { + fn from(app: &App) -> Self { + let library = utils::load_library(); + + let world = app.world(); + + let display_handle: &DisplayHandleWrapper = + world.get_resource::().unwrap(); + + let enabled_extensions = Surface::required_extensions(&display_handle.0).unwrap(); + + let instance = utils::create_instance(library.clone(), enabled_extensions); + + let (device, mut queues) = utils::pick_graphics_device(&instance, &display_handle.0); + let graphics_queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance: instance.clone(), + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + } + } +} diff --git a/src/core/vulkan/window_render_context.rs b/src/core/vulkan/window_render_context.rs new file mode 100644 index 0000000..1c653a7 --- /dev/null +++ b/src/core/vulkan/window_render_context.rs @@ -0,0 +1,117 @@ +use bevy_ecs::system::Resource; +use std::sync::Arc; +use vulkano::image::view::ImageView; +use vulkano::image::{Image, ImageUsage}; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::swapchain::{Swapchain, SwapchainCreateInfo}; +use vulkano::sync::{self, GpuFuture}; +use vulkano::{Validated, VulkanError}; +use winit::window::Window; + +use crate::core::app::App; +use crate::core::window::raw_handle::WindowWrapper; + +use super::vulkan_context::VulkanContext; + +#[derive(Resource)] +pub struct WindowRenderContext { + pub window: Arc, + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, +} + +impl From<&App> for WindowRenderContext { + fn from(app: &App) -> Self { + let world = app.world(); + let vulkan_context = world.get_resource::().unwrap(); + let window_handle = world.get_resource::().unwrap(); + let window_size = window_handle.0.inner_size(); + + let surface = vulkan_context.create_surface(window_handle.0.clone()); + + let (swapchain, images) = { + let surface_capabilities = vulkan_context + .device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + let (image_format, _) = vulkan_context + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + vulkan_context.device.clone(), + surface, + SwapchainCreateInfo { + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + let attachment_image_views = window_size_dependent_setup(&images); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let recreate_swapchain = false; + let previous_frame_end = Some(sync::now(vulkan_context.device.clone()).boxed_send_sync()); + + Self { + window: window_handle.0.clone(), + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + } + } +} + +impl WindowRenderContext { + pub fn update_swapchain(&mut self) -> Result<(), Validated> { + if !self.recreate_swapchain { + return Ok(()); + } + + let window_size = self.window.inner_size(); + let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..self.swapchain.create_info() + })?; + + self.swapchain = new_swapchain; + self.attachment_image_views = window_size_dependent_setup(&new_images); + self.viewport.extent = window_size.into(); + self.recreate_swapchain = false; + + Ok(()) + } +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/core/window/config.rs b/src/core/window/config.rs new file mode 100644 index 0000000..8fbef8c --- /dev/null +++ b/src/core/window/config.rs @@ -0,0 +1,17 @@ +use bevy_ecs::system::Resource; +use winit::{dpi::PhysicalSize, window::WindowAttributes}; + +#[derive(Resource, Clone)] +pub struct WindowConfig { + pub title: String, + pub width: u32, + pub height: u32, +} + +impl Into for &WindowConfig { + fn into(self) -> WindowAttributes { + WindowAttributes::default() + .with_title(self.title.clone()) + .with_inner_size(PhysicalSize::new(self.width as f64, self.height as f64)) + } +} diff --git a/src/core/window/mod.rs b/src/core/window/mod.rs new file mode 100644 index 0000000..4368310 --- /dev/null +++ b/src/core/window/mod.rs @@ -0,0 +1,48 @@ +use config::WindowConfig; +use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper}; +use state::WindowState; +use winit::event_loop::EventLoop; + +use super::app::{App, AppExit}; + +pub mod config; +pub mod raw_handle; +pub mod state; + +#[derive(Debug, thiserror::Error)] +pub enum WindowError { + #[error("Failed to create event loop")] + FailedToCreateEventLoop, +} + +pub struct Window; + +impl Window { + pub fn new(app: &mut App, window_config: WindowConfig) -> Result<(), WindowError> { + let world = app.world_mut(); + world.insert_resource(window_config); + + let mut event_loop_builder = EventLoop::with_user_event(); + let event_loop = event_loop_builder + .build() + .map_err(|_| WindowError::FailedToCreateEventLoop)?; + + world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle())); + + app.set_runner(Box::new(move |app| runner(app, event_loop))); + + Ok(()) + } +} + +fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit { + app.world_mut() + .insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy())); + + let mut window_state = WindowState::new(app); + + match event_loop.run_app(&mut window_state) { + Ok(_) => AppExit::Success, + Err(e) => AppExit::Error(Box::new(e)), + } +} diff --git a/src/core/window/raw_handle.rs b/src/core/window/raw_handle.rs new file mode 100644 index 0000000..ad9c8dd --- /dev/null +++ b/src/core/window/raw_handle.rs @@ -0,0 +1,23 @@ +use std::sync::Arc; + +use bevy_ecs::system::Resource; +use winit::{event_loop::EventLoopProxy, window::Window}; + +#[derive(Resource)] +pub struct EventLoopProxyWrapper(EventLoopProxy); + +impl EventLoopProxyWrapper { + pub fn new(event_loop: EventLoopProxy) -> Self { + Self(event_loop) + } + + pub fn proxy(&self) -> &EventLoopProxy { + &self.0 + } +} + +#[derive(Resource)] +pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle); + +#[derive(Resource)] +pub struct WindowWrapper(pub Arc); diff --git a/src/core/window/state.rs b/src/core/window/state.rs new file mode 100644 index 0000000..0b0966e --- /dev/null +++ b/src/core/window/state.rs @@ -0,0 +1,46 @@ +use std::sync::Arc; + +use bevy_ecs::world::World; +use winit::{ + application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop, + window::WindowId, +}; + +use crate::core::app::App; + +use super::{config::WindowConfig, raw_handle::WindowWrapper}; + +pub struct WindowState { + app: App, +} + +impl WindowState { + pub fn new(app: App) -> Self { + Self { app } + } + + fn world(&self) -> &World { + self.app.world() + } +} + +impl ApplicationHandler for WindowState { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_config = self.world().get_resource::().unwrap(); + + let window = event_loop.create_window(window_config.into()).unwrap(); + self.app + .world_mut() + .insert_resource(WindowWrapper(Arc::new(window))); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + _ => {} + } + } +} diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..0b7787d --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1,16 @@ +use crate::core::{ + app::App, + vulkan::Vulkan, + window::{Window, config::WindowConfig}, +}; + +pub fn init(app: &mut App) { + let window_config = WindowConfig { + title: "Rust ASH Test".to_string(), + width: 800, + height: 600, + }; + + Window::new(app, window_config).unwrap(); + Vulkan::new(app).unwrap(); +} diff --git a/src/main.rs b/src/main.rs index ac0d7c9..8297797 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,29 @@ -use winit::event_loop::EventLoop; +use std::error::Error; +use winit::event_loop::{ControlFlow, EventLoop}; -mod renderer; -mod vulkan; +pub mod core; +pub mod game; +pub mod old_app; -use renderer::app::App; -use vulkan::context::VulkanContext; +fn main() -> Result<(), impl Error> { + env_logger::init(); -fn main() { - let event_loop = EventLoop::new().unwrap(); - let vulkan_context = VulkanContext::new(&event_loop).unwrap(); - let mut app = App::new(vulkan_context); - event_loop.run_app(&mut app).unwrap(); + run_new_app() + // run_old_app() +} + +fn run_new_app() -> Result<(), impl Error> { + let mut app = core::app::App::default(); + game::init(&mut app); + app.run() +} + +fn run_old_app() -> Result<(), impl Error> { + let event_loop = EventLoop::new().unwrap(); + event_loop.set_control_flow(ControlFlow::Poll); + + let vulkan_context = old_app::vulkan_context::VulkanContext::from(&event_loop); + let mut app = old_app::app::App::from(vulkan_context); + + event_loop.run_app(&mut app) } diff --git a/src/old_app/app.rs b/src/old_app/app.rs new file mode 100644 index 0000000..cfcf038 --- /dev/null +++ b/src/old_app/app.rs @@ -0,0 +1,178 @@ +use crate::old_app::scene::Scene; +use crate::old_app::vulkan_context::VulkanContext; +use crate::old_app::window_render_context::WindowRenderContext; +use std::sync::Arc; +use vulkano::command_buffer::{RenderingAttachmentInfo, RenderingInfo}; +use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; +use vulkano::swapchain::{SwapchainPresentInfo, acquire_next_image}; +use vulkano::sync::GpuFuture; +use vulkano::{Validated, VulkanError, sync}; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::ActiveEventLoop; +use winit::window::WindowId; + +pub struct App { + vulkan_context: VulkanContext, + window_render_context: Option, + scene: Option, +} + +impl From for App { + fn from(vulkan_context: VulkanContext) -> Self { + Self { + vulkan_context, + window_render_context: None, + scene: None, + } + } +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let window_attributes = winit::window::Window::default_attributes() + .with_title("Rust ASH Test") + .with_inner_size(winit::dpi::PhysicalSize::new( + f64::from(800), + f64::from(600), + )); + + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); + + let surface = self.vulkan_context.create_surface(window.clone()); + + self.window_render_context = Some(WindowRenderContext::new( + window, + surface, + &self.vulkan_context.device, + )); + self.scene = Some( + Scene::load( + &self.vulkan_context, + self.window_render_context.as_ref().unwrap(), + ) + .unwrap(), + ); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => { + log::debug!("The close button was pressed; stopping"); + event_loop.exit(); + } + WindowEvent::Resized(_) => { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.recreate_swapchain = true; + } + WindowEvent::RedrawRequested => { + let (image_index, acquire_future) = { + let rcx = self.window_render_context.as_mut().unwrap(); + let window_size = rcx.window.inner_size(); + + if window_size.width == 0 || window_size.height == 0 { + return; + } + + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); + rcx.update_swapchain().unwrap(); + + let (image_index, suboptimal, acquire_future) = + match acquire_next_image(rcx.swapchain.clone(), None) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; + + if suboptimal { + rcx.recreate_swapchain = true; + } + + (image_index, acquire_future) + }; + + let mut builder = self.vulkan_context.create_render_builder(); + + { + let rcx = self.window_render_context.as_ref().unwrap(); + builder + .begin_rendering(RenderingInfo { + color_attachments: vec![Some(RenderingAttachmentInfo { + load_op: AttachmentLoadOp::Clear, + store_op: AttachmentStoreOp::Store, + clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), + ..RenderingAttachmentInfo::image_view( + rcx.attachment_image_views[image_index as usize].clone(), + ) + })], + ..Default::default() + }) + .unwrap() + .set_viewport(0, [rcx.viewport.clone()].into_iter().collect()) + .unwrap(); + } + + if let Some(scene) = self.scene.as_ref() { + scene + .render( + &self.vulkan_context, + &self.window_render_context.as_ref().unwrap(), + &mut builder, + ) + .unwrap(); + } + + builder.end_rendering().unwrap(); + + let command_buffer = builder.build().unwrap(); + + { + let rcx = self.window_render_context.as_mut().unwrap(); + + let future = rcx + .previous_frame_end + .take() + .unwrap() + .join(acquire_future) + .then_execute(self.vulkan_context.graphics_queue.clone(), command_buffer) + .unwrap() + .then_swapchain_present( + self.vulkan_context.graphics_queue.clone(), + SwapchainPresentInfo::swapchain_image_index( + rcx.swapchain.clone(), + image_index, + ), + ) + .then_signal_fence_and_flush(); + + match future.map_err(Validated::unwrap) { + Ok(future) => { + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + Err(e) => { + println!("failed to flush future: {e}"); + rcx.previous_frame_end = + Some(sync::now(self.vulkan_context.device.clone()).boxed()); + } + } + } + } + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { + let rcx = self.window_render_context.as_mut().unwrap(); + rcx.window.request_redraw(); + } +} diff --git a/src/old_app/mod.rs b/src/old_app/mod.rs new file mode 100644 index 0000000..9fce0a9 --- /dev/null +++ b/src/old_app/mod.rs @@ -0,0 +1,5 @@ +pub mod app; +pub mod pipelines; +pub mod scene; +pub mod vulkan_context; +pub mod window_render_context; diff --git a/src/old_app/pipelines/mod.rs b/src/old_app/pipelines/mod.rs new file mode 100644 index 0000000..e5f30a7 --- /dev/null +++ b/src/old_app/pipelines/mod.rs @@ -0,0 +1 @@ +pub mod triangle_pipeline; diff --git a/src/old_app/pipelines/triangle_pipeline.rs b/src/old_app/pipelines/triangle_pipeline.rs new file mode 100644 index 0000000..e573747 --- /dev/null +++ b/src/old_app/pipelines/triangle_pipeline.rs @@ -0,0 +1,111 @@ +use std::collections::BTreeMap; +use std::error::Error; +use std::sync::Arc; +use vulkano::descriptor_set::layout::{ + DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, +}; +use vulkano::device::Device; +use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; +use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; +use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; +use vulkano::pipeline::graphics::multisample::MultisampleState; +use vulkano::pipeline::graphics::rasterization::RasterizationState; +use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; +use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; +use vulkano::pipeline::graphics::viewport::ViewportState; +use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; +use vulkano::pipeline::{ + DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, +}; +use vulkano::shader::{EntryPoint, ShaderStages}; +use vulkano::swapchain::Swapchain; + +use crate::core::render::vertex::Vertex2D; + +pub mod shaders { + pub mod vs { + vulkano_shaders::shader! { + ty: "vertex", + path: r"res/shaders/vertex.vert", + } + } + + pub mod fs { + vulkano_shaders::shader! { + ty: "fragment", + path: r"res/shaders/vertex.frag", + } + } +} + +pub fn create_triangle_pipeline( + device: &Arc, + swapchain: &Arc, +) -> Result, Box> { + let (vs, fs) = load_shaders(device)?; + let vertex_input_state = Vertex2D::per_vertex().definition(&vs)?; + + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + ]; + + let mut bindings = BTreeMap::::new(); + let mut descriptor_set_layout_binding = + DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); + descriptor_set_layout_binding.stages = ShaderStages::VERTEX; + bindings.insert(0, descriptor_set_layout_binding); + + let descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings, + ..Default::default() + }; + + let create_info = PipelineDescriptorSetLayoutCreateInfo { + set_layouts: vec![descriptor_set_layout], + flags: PipelineLayoutCreateFlags::default(), + push_constant_ranges: vec![], + } + .into_pipeline_layout_create_info(device.clone())?; + + let layout = PipelineLayout::new(device.clone(), create_info)?; + + let subpass = PipelineRenderingCreateInfo { + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + + let pipeline = GraphicsPipeline::new( + device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState::default()), + viewport_state: Some(ViewportState::default()), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.color_attachment_formats.len() as u32, + ColorBlendAttachmentState::default(), + )), + dynamic_state: [DynamicState::Viewport].into_iter().collect(), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + )?; + + Ok(pipeline) +} + +fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { + let vs = shaders::vs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of vertex shader".to_string())?; + + let fs = shaders::fs::load(device.clone())? + .entry_point("main") + .ok_or("Failed find main entry point of fragment shader".to_string())?; + + Ok((vs, fs)) +} diff --git a/src/old_app/scene.rs b/src/old_app/scene.rs new file mode 100644 index 0000000..72e3f48 --- /dev/null +++ b/src/old_app/scene.rs @@ -0,0 +1,175 @@ +use crate::old_app::pipelines::triangle_pipeline::shaders::vs; +use glam::{Mat3, Mat4, Vec3}; +use std::error::Error; +use std::sync::Arc; +use std::time::Instant; +use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; +use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; +use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; +use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; + +use crate::core::render::vertex::Vertex2D; +use crate::old_app::pipelines::triangle_pipeline::create_triangle_pipeline; + +use super::vulkan_context::VulkanContext; +use super::window_render_context::WindowRenderContext; + +const VERTICES: [Vertex2D; 12] = [ + // Triangle en haut à gauche + Vertex2D { + position: [-0.5, -0.75], + color: [1.0, 0.0, 0.0], + }, + Vertex2D { + position: [-0.75, -0.25], + color: [0.0, 1.0, 0.0], + }, + Vertex2D { + position: [-0.25, -0.25], + color: [0.0, 0.0, 1.0], + }, + // Triangle en bas à gauche + Vertex2D { + position: [-0.5, 0.25], + color: [0.5, 0.5, 0.5], + }, + Vertex2D { + position: [-0.75, 0.75], + color: [0.2, 0.8, 0.2], + }, + Vertex2D { + position: [-0.25, 0.75], + color: [0.8, 0.2, 0.2], + }, + // Triangle en haut à droite + Vertex2D { + position: [0.5, -0.75], + color: [1.0, 1.0, 0.0], + }, + Vertex2D { + position: [0.25, -0.25], + color: [0.0, 1.0, 1.0], + }, + Vertex2D { + position: [0.75, -0.25], + color: [1.0, 0.0, 1.0], + }, + // Triangle en bas à droite + Vertex2D { + position: [0.5, 0.25], + color: [0.1, 0.5, 0.8], + }, + Vertex2D { + position: [0.25, 0.75], + color: [0.8, 0.6, 0.1], + }, + Vertex2D { + position: [0.75, 0.75], + color: [0.3, 0.4, 0.6], + }, +]; + +pub struct Scene { + pipeline: Arc, + vertex_buffer: Subbuffer<[Vertex2D]>, + + rotation_start: Instant, +} + +impl Scene { + pub fn load( + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Result> { + let pipeline = + create_triangle_pipeline(&vulkan_context.device, &window_render_context.swapchain)?; + let vertex_buffer = + Vertex2D::create_buffer(Vec::from_iter(VERTICES), &vulkan_context.memory_allocator)?; + + Ok(Scene { + pipeline, + vertex_buffer, + rotation_start: Instant::now(), + }) + } + + pub fn render( + &self, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + builder: &mut AutoCommandBufferBuilder, + ) -> Result<(), Box> { + let vertex_count = self.vertex_buffer.len() as u32; + let instance_count = vertex_count / 3; + + let uniform_buffer = self.get_uniform_buffer(vulkan_context, window_render_context); + let layout = &self.pipeline.layout().set_layouts()[0]; + let descriptor_set = DescriptorSet::new( + vulkan_context.descriptor_set_allocator.clone(), + layout.clone(), + [WriteDescriptorSet::buffer(0, uniform_buffer)], + [], + ) + .unwrap(); + + unsafe { + builder + .bind_pipeline_graphics(self.pipeline.clone())? + .bind_descriptor_sets( + PipelineBindPoint::Graphics, + self.pipeline.layout().clone(), + 0, + descriptor_set, + )? + .bind_vertex_buffers(0, self.vertex_buffer.clone())? + .draw(vertex_count, instance_count, 0, 0)?; + } + + Ok(()) + } + + fn get_uniform_buffer( + &self, + vulkan_context: &VulkanContext, + window_render_context: &WindowRenderContext, + ) -> Subbuffer { + let swapchain = &window_render_context.swapchain; + let elapsed = self.rotation_start.elapsed(); + let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0; + let rotation = Mat3::from_rotation_y(rotation as f32); + + // NOTE: This teapot was meant for OpenGL where the origin is at the lower left + // instead the origin is at the upper left in Vulkan, so we reverse the Y axis. + let aspect_ratio = swapchain.image_extent()[0] as f32 / swapchain.image_extent()[1] as f32; + + let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0); + let view = Mat4::look_at_rh( + Vec3::new(0.3, 0.3, 1.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, -1.0, 0.0), + ); + let scale = Mat4::from_scale(Vec3::splat(1.0)); + + let uniform_data = vs::MVPData { + world: Mat4::from_mat3(rotation).to_cols_array_2d(), + view: (view * scale).to_cols_array_2d(), + projection: proj.to_cols_array_2d(), + }; + + Buffer::from_data( + vulkan_context.memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::UNIFORM_BUFFER, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + uniform_data, + ) + .unwrap() + } +} diff --git a/src/old_app/vulkan_context.rs b/src/old_app/vulkan_context.rs new file mode 100644 index 0000000..bad47de --- /dev/null +++ b/src/old_app/vulkan_context.rs @@ -0,0 +1,212 @@ +use std::{any::Any, sync::Arc}; + +use vulkano::{ + Version, VulkanLibrary, + command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + allocator::StandardCommandBufferAllocator, + }, + descriptor_set::allocator::StandardDescriptorSetAllocator, + device::{ + Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, + QueueFlags, + physical::{PhysicalDevice, PhysicalDeviceType}, + }, + instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions}, + memory::allocator::StandardMemoryAllocator, + swapchain::Surface, +}; +use winit::{ + event_loop::EventLoop, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, +}; + +pub struct VulkanContext { + instance: Arc, + pub device: Arc, + pub graphics_queue: Arc, + + pub memory_allocator: Arc, + pub command_buffer_allocator: Arc, + pub descriptor_set_allocator: Arc, +} + +impl From<&EventLoop<()>> for VulkanContext { + fn from(event_loop: &EventLoop<()>) -> Self { + let library = load_library(); + + let enabled_extensions = Surface::required_extensions(event_loop).unwrap(); + + let instance = create_instance(library.clone(), enabled_extensions); + + let (device, mut queues) = pick_graphics_device(&instance, event_loop); + let graphics_queue = queues.next().unwrap(); + + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); + + let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( + device.clone(), + Default::default(), + )); + + let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( + device.clone(), + Default::default(), + )); + + Self { + instance, + device, + graphics_queue, + memory_allocator, + command_buffer_allocator, + descriptor_set_allocator, + } + } +} + +impl VulkanContext { + pub fn create_surface( + &self, + window: Arc, + ) -> Arc { + Surface::from_window(self.instance.clone(), window).unwrap() + } + + pub fn create_render_builder(&self) -> AutoCommandBufferBuilder { + AutoCommandBufferBuilder::primary( + self.command_buffer_allocator.clone(), + self.graphics_queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap() + } +} + +fn load_library() -> Arc { + let library = VulkanLibrary::new().unwrap(); + + log::debug!("Available layer:"); + for layer in library.layer_properties().unwrap() { + log::debug!( + "\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}", + layer.name(), + layer.description(), + layer.implementation_version(), + layer.vulkan_version() + ); + } + + library +} + +fn create_instance( + library: Arc, + required_extensions: InstanceExtensions, +) -> Arc { + Instance::new( + library, + InstanceCreateInfo { + // Enable enumerating devices that use non-conformant Vulkan implementations. + // (e.g. MoltenVK) + flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, + enabled_extensions: required_extensions, + enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], + ..Default::default() + }, + ) + .unwrap() +} + +fn find_physical_device_queue_family_indexes( + physical_device: &Arc, + event_loop: &EventLoop<()>, +) -> Option { + let mut graphic_queue_family_index = None; + + for (i, queue_family_property) in physical_device.queue_family_properties().iter().enumerate() { + if queue_family_property + .queue_flags + .intersects(QueueFlags::GRAPHICS) + && physical_device + .presentation_support(i as u32, event_loop) + .unwrap() + { + graphic_queue_family_index = Some(i as u32); + } + } + + graphic_queue_family_index +} + +fn pick_physical_device_and_queue_family_indexes( + instance: &Arc, + event_loop: &EventLoop<()>, + device_extensions: &DeviceExtensions, +) -> Option<(Arc, u32)> { + instance + .enumerate_physical_devices() + .unwrap() + .filter(|p| { + p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering + }) + .filter(|p| p.supported_extensions().contains(device_extensions)) + .filter_map(|p| { + find_physical_device_queue_family_indexes(&p, event_loop) + .and_then(|indexes| Some((p, indexes))) + }) + .min_by_key(|(p, _)| match p.properties().device_type { + PhysicalDeviceType::DiscreteGpu => 0, + PhysicalDeviceType::IntegratedGpu => 1, + PhysicalDeviceType::VirtualGpu => 2, + PhysicalDeviceType::Cpu => 3, + PhysicalDeviceType::Other => 4, + _ => 5, + }) +} + +fn pick_graphics_device( + instance: &Arc, + event_loop: &EventLoop<()>, +) -> ( + Arc, + impl ExactSizeIterator> + use<>, +) { + let mut device_extensions = DeviceExtensions { + khr_swapchain: true, + ..DeviceExtensions::empty() + }; + + let (physical_device, graphics_family_index) = + pick_physical_device_and_queue_family_indexes(instance, event_loop, &device_extensions) + .unwrap(); + + log::debug!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + + if physical_device.api_version() < Version::V1_3 { + device_extensions.khr_dynamic_rendering = true; + } + + log::debug!("Using device extensions: {:#?}", device_extensions); + + Device::new( + physical_device, + DeviceCreateInfo { + queue_create_infos: vec![QueueCreateInfo { + queue_family_index: graphics_family_index, + ..Default::default() + }], + enabled_extensions: device_extensions, + enabled_features: DeviceFeatures { + dynamic_rendering: true, + ..DeviceFeatures::empty() + }, + ..Default::default() + }, + ) + .unwrap() +} diff --git a/src/old_app/window_render_context.rs b/src/old_app/window_render_context.rs new file mode 100644 index 0000000..54120d0 --- /dev/null +++ b/src/old_app/window_render_context.rs @@ -0,0 +1,102 @@ +use std::sync::Arc; +use vulkano::device::Device; +use vulkano::image::view::ImageView; +use vulkano::image::{Image, ImageUsage}; +use vulkano::pipeline::graphics::viewport::Viewport; +use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo}; +use vulkano::sync::GpuFuture; +use vulkano::{Validated, VulkanError, sync}; +use winit::window::Window; + +pub struct WindowRenderContext { + pub window: Arc, + pub swapchain: Arc, + pub attachment_image_views: Vec>, + pub viewport: Viewport, + pub recreate_swapchain: bool, + pub previous_frame_end: Option>, +} + +impl WindowRenderContext { + pub fn new(window: Arc, surface: Arc, device: &Arc) -> Self { + let window_size = window.inner_size(); + + let (swapchain, images) = { + let surface_capabilities = device + .physical_device() + .surface_capabilities(&surface, Default::default()) + .unwrap(); + + let (image_format, _) = device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap()[0]; + + Swapchain::new( + device.clone(), + surface, + SwapchainCreateInfo { + // 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works. + min_image_count: surface_capabilities.min_image_count.max(2), + image_format, + image_extent: window_size.into(), + image_usage: ImageUsage::COLOR_ATTACHMENT, + composite_alpha: surface_capabilities + .supported_composite_alpha + .into_iter() + .next() + .unwrap(), + + ..Default::default() + }, + ) + .unwrap() + }; + + let attachment_image_views = window_size_dependent_setup(&images); + + let viewport = Viewport { + offset: [0.0, 0.0], + extent: window_size.into(), + depth_range: 0.0..=1.0, + }; + + let recreate_swapchain = false; + let previous_frame_end = Some(sync::now(device.clone()).boxed()); + + Self { + window, + swapchain, + attachment_image_views, + viewport, + recreate_swapchain, + previous_frame_end, + } + } + + pub fn update_swapchain(&mut self) -> Result<(), Validated> { + if !self.recreate_swapchain { + return Ok(()); + } + + let window_size = self.window.inner_size(); + let (new_swapchain, new_images) = self.swapchain.recreate(SwapchainCreateInfo { + image_extent: window_size.into(), + ..self.swapchain.create_info() + })?; + + self.swapchain = new_swapchain; + self.attachment_image_views = window_size_dependent_setup(&new_images); + self.viewport.extent = window_size.into(); + self.recreate_swapchain = false; + + Ok(()) + } +} + +fn window_size_dependent_setup(images: &[Arc]) -> Vec> { + images + .iter() + .map(|image| ImageView::new_default(image.clone()).unwrap()) + .collect::>() +} diff --git a/src/renderer/app.rs b/src/renderer/app.rs deleted file mode 100644 index 923d70c..0000000 --- a/src/renderer/app.rs +++ /dev/null @@ -1,262 +0,0 @@ -use crate::renderer::components::{Entity, Material, Mesh, Transform}; -use crate::vulkan::context::VulkanContext; -use crate::vulkan::pipeline::{Pipeline, triangle::TrianglePipeline}; -use crate::vulkan::renderer::VulkanRenderer; -use crate::vulkan::resources::vertex::{MVPData, Vertex2D}; -use std::error::Error; -use std::sync::Arc; -use vulkano::VulkanError; -use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage}; -use vulkano::command_buffer::{ - AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, - RenderingAttachmentInfo, RenderingInfo, -}; -use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter}; -use vulkano::pipeline::{Pipeline as VulkanPipeline, PipelineBindPoint}; -use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp}; -use vulkano::swapchain::Surface; -use winit::application::ApplicationHandler; -use winit::event::WindowEvent; -use winit::event_loop::ActiveEventLoop; -use winit::window::WindowId; - -pub struct App { - pub vulkan_context: VulkanContext, - pub renderer: Option, - pub entities: Vec, -} - -impl App { - pub fn new(vulkan_context: VulkanContext) -> Self { - Self { - vulkan_context, - renderer: None, - entities: Vec::new(), - } - } - - pub fn setup_test_entities(&mut self) -> Result<(), Box> { - // Créer un pipeline de test - let pipeline = TrianglePipeline::new(&self.vulkan_context.device); - - // Créer un buffer de vertex pour un triangle - let vertices = [ - Vertex2D { - position: [-0.5, -0.5], - color: [1.0, 0.0, 0.0], // Rouge - }, - Vertex2D { - position: [0.5, -0.5], - color: [0.0, 1.0, 0.0], // Vert - }, - Vertex2D { - position: [0.0, 0.5], - color: [0.0, 0.0, 1.0], // Bleu - }, - ]; - - let vertex_buffer = - Vertex2D::create_buffer(vertices.to_vec(), &self.vulkan_context.memory_allocator) - .unwrap(); - - // Créer un buffer uniform pour les matrices MVP - let mvp_data = MVPData { - world: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - ], - view: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - ], - projection: [ - [1.0, 0.0, 0.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - ], - }; - - let uniform_buffer = Buffer::from_data( - self.vulkan_context.memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - mvp_data, - ) - .unwrap(); - - // Créer un descriptor set de test - let descriptor_set = DescriptorSet::new( - self.vulkan_context.descriptor_set_allocator.clone(), - pipeline - .get_pipeline() - .layout() - .set_layouts() - .get(0) - .unwrap() - .clone(), - [WriteDescriptorSet::buffer(0, uniform_buffer)], - [], - )?; - - let material = Material { - pipeline: pipeline.get_pipeline().clone(), - descriptor_set, - }; - - // Créer quelques entités de test - let mut entities = Vec::new(); - for i in 0..3 { - entities.push(Entity { - mesh: Mesh { - vertex_buffer: vertex_buffer.clone(), - vertex_count: 3, - instance_count: 1, - }, - material: material.clone(), - transform: Transform { - position: [i as f32 * 0.5 - 0.5, 0.0, 0.0], - rotation: [0.0, 0.0, 0.0], - scale: [1.0, 1.0, 1.0], - }, - }); - } - self.entities = entities; - - Ok(()) - } - - pub fn render( - &self, - command_buffer: &mut AutoCommandBufferBuilder, - ) -> Result<(), Box> { - for entity in &self.entities { - command_buffer - .bind_pipeline_graphics(entity.material.pipeline.clone()) - .unwrap() - .bind_descriptor_sets( - PipelineBindPoint::Graphics, - entity.material.pipeline.layout().clone(), - 0, - entity.material.descriptor_set.clone(), - ) - .unwrap() - .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone()) - .unwrap(); - unsafe { - command_buffer - .draw(entity.mesh.vertex_count, 1, 0, 0) - .unwrap(); - } - } - Ok(()) - } -} - -impl ApplicationHandler for App { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let window_attributes = winit::window::Window::default_attributes() - .with_title("Rust ASH Test") - .with_inner_size(winit::dpi::PhysicalSize::new( - f64::from(800), - f64::from(600), - )); - - let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); - - let surface = - Surface::from_window(self.vulkan_context.instance.clone(), window.clone()).unwrap(); - - self.renderer = Some(VulkanRenderer::new( - window, - surface, - self.vulkan_context.device.clone(), - self.vulkan_context.queue.clone(), - )); - - self.setup_test_entities().unwrap(); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => { - log::debug!("The close button was pressed; stopping"); - event_loop.exit(); - } - WindowEvent::Resized(_) => { - let renderer = self.renderer.as_mut().unwrap(); - renderer.recreate_swapchain = true; - } - WindowEvent::RedrawRequested => { - let (image_index, acquire_future) = - match self.renderer.as_mut().unwrap().begin_frame() { - Ok(r) => r, - Err(VulkanError::OutOfDate) => return, - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - let mut builder = AutoCommandBufferBuilder::primary( - self.vulkan_context.command_buffer_allocator.clone(), - self.vulkan_context.queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - - { - builder - .begin_rendering(RenderingInfo { - color_attachments: vec![Some(RenderingAttachmentInfo { - load_op: AttachmentLoadOp::Clear, - store_op: AttachmentStoreOp::Store, - clear_value: Some([0.0, 0.0, 0.0, 1.0].into()), - ..RenderingAttachmentInfo::image_view( - self.renderer.as_ref().unwrap().attachment_image_views - [image_index as usize] - .clone(), - ) - })], - ..Default::default() - }) - .unwrap() - .set_viewport( - 0, - [self.renderer.as_ref().unwrap().viewport.clone()] - .into_iter() - .collect(), - ) - .unwrap(); - } - - self.render(&mut builder).unwrap(); - - { - builder.end_rendering().unwrap(); - } - - self.renderer - .as_mut() - .unwrap() - .end_frame(image_index, acquire_future, builder) - .unwrap(); - } - _ => {} - } - } - - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { - let renderer = self.renderer.as_mut().unwrap(); - renderer.window.request_redraw(); - } -} diff --git a/src/renderer/components.rs b/src/renderer/components.rs deleted file mode 100644 index 968539e..0000000 --- a/src/renderer/components.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::sync::Arc; -use vulkano::buffer::Subbuffer; -use vulkano::buffer::allocator::SubbufferAllocator; -use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use vulkano::descriptor_set::DescriptorSet; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::Device; -use vulkano::memory::allocator::StandardMemoryAllocator; -use vulkano::pipeline::GraphicsPipeline; -use vulkano::pipeline::Pipeline; - -use crate::vulkan::resources::vertex::Vertex2D; - -#[derive(Clone)] -pub struct Mesh { - pub vertex_buffer: Subbuffer<[Vertex2D]>, - pub vertex_count: u32, - pub instance_count: u32, -} - -#[derive(Clone)] -pub struct Material { - pub pipeline: Arc, - pub descriptor_set: Arc, -} - -#[derive(Clone)] -pub struct Transform { - pub position: [f32; 3], - pub rotation: [f32; 3], - pub scale: [f32; 3], -} - -#[derive(Clone)] -pub struct RenderResources { - pub device: Arc, - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, -} - -pub struct Entity { - pub mesh: Mesh, - pub material: Material, - pub transform: Transform, -} - -pub fn render_system( - _resources: &RenderResources, - builder: &mut AutoCommandBufferBuilder, - entities: &[Entity], -) -> Result<(), Box> { - for entity in entities { - unsafe { - builder - .bind_pipeline_graphics(entity.material.pipeline.clone())? - .bind_descriptor_sets( - vulkano::pipeline::PipelineBindPoint::Graphics, - entity.material.pipeline.layout().clone(), - 0, - entity.material.descriptor_set.clone(), - )? - .bind_vertex_buffers(0, entity.mesh.vertex_buffer.clone())? - .draw(entity.mesh.vertex_count, entity.mesh.instance_count, 0, 0)?; - } - } - - Ok(()) -} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs deleted file mode 100644 index 13545c4..0000000 --- a/src/renderer/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod app; -pub mod components; diff --git a/src/vulkan/context.rs b/src/vulkan/context.rs deleted file mode 100644 index bd1aeba..0000000 --- a/src/vulkan/context.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::sync::Arc; -use vulkano::buffer::BufferUsage; -use vulkano::buffer::allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo}; -use vulkano::command_buffer::allocator::StandardCommandBufferAllocator; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::device::physical::PhysicalDeviceType; -use vulkano::device::{ - Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo, QueueFlags, -}; -use vulkano::instance::{Instance, InstanceCreateFlags, InstanceCreateInfo}; -use vulkano::memory::allocator::{MemoryTypeFilter, StandardMemoryAllocator}; -use vulkano::swapchain::Surface; -use vulkano::{Version, VulkanLibrary}; -use winit::event_loop::EventLoop; - -pub struct VulkanContext { - pub instance: Arc, - pub device: Arc, - pub queue: Arc, - pub memory_allocator: Arc, - pub command_buffer_allocator: Arc, - pub uniform_buffer_allocator: Arc, - pub descriptor_set_allocator: Arc, -} - -impl VulkanContext { - pub fn new(event_loop: &EventLoop<()>) -> Result> { - let library = VulkanLibrary::new().unwrap(); - for layer in library.layer_properties().unwrap() { - log::debug!("Available layer: {}", layer.name()); - } - - let required_extensions = Surface::required_extensions(event_loop).unwrap(); - let instance = Instance::new( - library, - InstanceCreateInfo { - flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, - enabled_extensions: required_extensions, - enabled_layers: vec![String::from("VK_LAYER_KHRONOS_validation")], - ..Default::default() - }, - ) - .unwrap(); - - let mut device_extensions = DeviceExtensions { - khr_swapchain: true, - ..DeviceExtensions::empty() - }; - - let (physical_device, queue_family_index) = instance - .enumerate_physical_devices() - .unwrap() - .filter(|p| { - p.api_version() >= Version::V1_3 || p.supported_extensions().khr_dynamic_rendering - }) - .filter(|p| p.supported_extensions().contains(&device_extensions)) - .filter_map(|p| { - p.queue_family_properties() - .iter() - .enumerate() - .position(|(_i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS)) - .map(|i| (p, i as u32)) - }) - .min_by_key(|(p, _)| match p.properties().device_type { - PhysicalDeviceType::DiscreteGpu => 0, - PhysicalDeviceType::IntegratedGpu => 1, - PhysicalDeviceType::VirtualGpu => 2, - PhysicalDeviceType::Cpu => 3, - PhysicalDeviceType::Other => 4, - _ => 5, - }) - .expect("no suitable physical device found"); - - log::debug!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); - - if physical_device.api_version() < Version::V1_3 { - device_extensions.khr_dynamic_rendering = true; - } - - log::debug!("Using device extensions: {:#?}", device_extensions); - - let (device, mut queues) = Device::new( - physical_device, - DeviceCreateInfo { - queue_create_infos: vec![QueueCreateInfo { - queue_family_index, - ..Default::default() - }], - enabled_extensions: device_extensions, - enabled_features: DeviceFeatures { - dynamic_rendering: true, - ..DeviceFeatures::empty() - }, - ..Default::default() - }, - ) - .unwrap(); - - let queue = queues.next().unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( - device.clone(), - Default::default(), - )); - - let uniform_buffer_allocator = Arc::new(SubbufferAllocator::new( - memory_allocator.clone(), - SubbufferAllocatorCreateInfo { - buffer_usage: BufferUsage::UNIFORM_BUFFER, - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - )); - - let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( - device.clone(), - Default::default(), - )); - - Ok(Self { - instance, - device, - queue, - memory_allocator, - command_buffer_allocator, - uniform_buffer_allocator, - descriptor_set_allocator, - }) - } -} diff --git a/src/vulkan/mod.rs b/src/vulkan/mod.rs deleted file mode 100644 index 8a4bdd9..0000000 --- a/src/vulkan/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod context; -pub mod pipeline; -pub mod renderer; -pub mod resources; diff --git a/src/vulkan/pipeline/mod.rs b/src/vulkan/pipeline/mod.rs deleted file mode 100644 index f9ba6d5..0000000 --- a/src/vulkan/pipeline/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub mod triangle; - -use std::sync::Arc; -use vulkano::device::Device; -use vulkano::pipeline::GraphicsPipeline; - -pub trait Pipeline { - fn create_pipeline(device: &Arc) -> Arc; - fn get_pipeline(&self) -> &Arc; -} - -pub struct PipelineManager { - pipelines: std::collections::HashMap>, -} - -impl PipelineManager { - pub fn new() -> Self { - Self { - pipelines: std::collections::HashMap::new(), - } - } - - pub fn register_pipeline(&mut self, name: String, pipeline: Arc) { - self.pipelines.insert(name, pipeline); - } - - pub fn get_pipeline(&self, name: &str) -> Option<&Arc> { - self.pipelines.get(name) - } -} diff --git a/src/vulkan/pipeline/triangle.rs b/src/vulkan/pipeline/triangle.rs deleted file mode 100644 index c625e66..0000000 --- a/src/vulkan/pipeline/triangle.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::sync::Arc; -use vulkano::descriptor_set::layout::{ - DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, -}; -use vulkano::device::Device; -use vulkano::pipeline::graphics::GraphicsPipelineCreateInfo; -use vulkano::pipeline::graphics::color_blend::{ColorBlendAttachmentState, ColorBlendState}; -use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; -use vulkano::pipeline::graphics::multisample::MultisampleState; -use vulkano::pipeline::graphics::rasterization::RasterizationState; -use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo; -use vulkano::pipeline::graphics::vertex_input::{Vertex, VertexDefinition}; -use vulkano::pipeline::graphics::viewport::ViewportState; -use vulkano::pipeline::layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags}; -use vulkano::pipeline::{ - DynamicState, GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo, -}; -use vulkano::shader::{EntryPoint, ShaderStages}; - -use crate::vulkan::resources::vertex::Vertex2D; - -use super::Pipeline; - -mod shaders { - pub mod vs { - vulkano_shaders::shader! { - ty: "vertex", - path: r"res/shaders/vertex.vert", - } - } - - pub mod fs { - vulkano_shaders::shader! { - ty: "fragment", - path: r"res/shaders/vertex.frag", - } - } -} - -pub struct TrianglePipeline { - pipeline: Arc, -} - -impl super::Pipeline for TrianglePipeline { - fn create_pipeline(device: &Arc) -> Arc { - let (vs, fs) = load_shaders(device).unwrap(); - let vertex_input_state = Vertex2D::per_vertex().definition(&vs).unwrap(); - - let stages = [ - PipelineShaderStageCreateInfo::new(vs), - PipelineShaderStageCreateInfo::new(fs), - ]; - - let mut bindings = BTreeMap::::new(); - let mut descriptor_set_layout_binding = - DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); - descriptor_set_layout_binding.stages = ShaderStages::VERTEX; - bindings.insert(0, descriptor_set_layout_binding); - - let descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings, - ..Default::default() - }; - - let create_info = PipelineDescriptorSetLayoutCreateInfo { - set_layouts: vec![descriptor_set_layout], - flags: PipelineLayoutCreateFlags::default(), - push_constant_ranges: vec![], - } - .into_pipeline_layout_create_info(device.clone()) - .unwrap(); - - let layout = PipelineLayout::new(device.clone(), create_info).unwrap(); - - let subpass = PipelineRenderingCreateInfo { - color_attachment_formats: vec![Some(vulkano::format::Format::B8G8R8A8_UNORM)], - ..Default::default() - }; - - GraphicsPipeline::new( - device.clone(), - None, - GraphicsPipelineCreateInfo { - stages: stages.into_iter().collect(), - vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), - viewport_state: Some(ViewportState::default()), - rasterization_state: Some(RasterizationState::default()), - multisample_state: Some(MultisampleState::default()), - color_blend_state: Some(ColorBlendState::with_attachment_states( - subpass.color_attachment_formats.len() as u32, - ColorBlendAttachmentState::default(), - )), - dynamic_state: [DynamicState::Viewport].into_iter().collect(), - subpass: Some(subpass.into()), - ..GraphicsPipelineCreateInfo::layout(layout) - }, - ) - .unwrap() - } - - fn get_pipeline(&self) -> &Arc { - &self.pipeline - } -} - -impl TrianglePipeline { - pub fn new(device: &Arc) -> Self { - Self { - pipeline: Self::create_pipeline(device), - } - } -} - -fn load_shaders(device: &Arc) -> Result<(EntryPoint, EntryPoint), Box> { - let vs = shaders::vs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of vertex shader".to_string())?; - - let fs = shaders::fs::load(device.clone())? - .entry_point("main") - .ok_or("Failed find main entry point of fragment shader".to_string())?; - - Ok((vs, fs)) -} diff --git a/src/vulkan/renderer.rs b/src/vulkan/renderer.rs deleted file mode 100644 index 079276b..0000000 --- a/src/vulkan/renderer.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::sync::Arc; -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; -use vulkano::device::Queue; -use vulkano::image::view::ImageView; -use vulkano::pipeline::graphics::viewport::Viewport; -use vulkano::swapchain::{Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo}; -use vulkano::sync::{self, GpuFuture}; -use vulkano::{Validated, VulkanError}; -use winit::window::Window; - -pub struct VulkanRenderer { - pub window: Arc, - pub surface: Arc, - pub swapchain: Arc, - pub queue: Arc, - pub attachment_image_views: Vec>, - pub previous_frame_end: Option>, - pub recreate_swapchain: bool, - pub viewport: Viewport, -} - -impl VulkanRenderer { - pub fn new( - window: Arc, - surface: Arc, - device: Arc, - queue: Arc, - ) -> Self { - let window_size = window.inner_size(); - let surface_formats = device - .physical_device() - .surface_formats(&surface, Default::default()) - .unwrap(); - let surface_format = surface_formats[0]; - - let (swapchain, images) = Swapchain::new( - device.clone(), - surface.clone(), - SwapchainCreateInfo { - image_extent: window_size.into(), - image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT, - image_format: surface_format.0, - ..Default::default() - }, - ) - .unwrap(); - - let attachment_image_views = images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect(); - - let viewport = Viewport { - offset: [0.0, 0.0], - extent: window_size.into(), - depth_range: 0.0..=1.0, - }; - - Self { - window, - surface, - swapchain, - queue, - attachment_image_views, - previous_frame_end: Some(sync::now(device).boxed()), - recreate_swapchain: false, - viewport, - } - } - - pub fn begin_frame(&mut self) -> Result<(u32, Box), VulkanError> { - self.previous_frame_end.as_mut().unwrap().cleanup_finished(); - - if self.recreate_swapchain { - self.recreate_swapchain(); - self.recreate_swapchain = false; - } - - let (image_index, suboptimal, acquire_future) = - match vulkano::swapchain::acquire_next_image(self.swapchain.clone(), None) - .map_err(Validated::unwrap) - { - Ok(r) => r, - Err(VulkanError::OutOfDate) => { - self.recreate_swapchain = true; - return Err(VulkanError::OutOfDate); - } - Err(e) => panic!("failed to acquire next image: {e}"), - }; - - if suboptimal { - self.recreate_swapchain = true; - } - - Ok((image_index, acquire_future.boxed())) - } - - pub fn end_frame( - &mut self, - image_index: u32, - acquire_future: Box, - command_buffer: AutoCommandBufferBuilder, - ) -> Result<(), VulkanError> { - let command_buffer = command_buffer.build().unwrap(); - let future = self - .previous_frame_end - .take() - .unwrap() - .join(acquire_future) - .then_execute(self.queue.clone(), command_buffer) - .unwrap() - .then_swapchain_present( - self.queue.clone(), - SwapchainPresentInfo::swapchain_image_index(self.swapchain.clone(), image_index), - ) - .then_signal_fence_and_flush(); - - match future.map_err(Validated::unwrap) { - Ok(future) => { - self.previous_frame_end = Some(future.boxed()); - Ok(()) - } - Err(VulkanError::OutOfDate) => { - self.recreate_swapchain = true; - self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed()); - Ok(()) - } - Err(e) => { - println!("failed to flush future: {e}"); - self.previous_frame_end = Some(sync::now(self.queue.device().clone()).boxed()); - Ok(()) - } - } - } - - fn recreate_swapchain(&mut self) { - let image_extent: [u32; 2] = self.window.inner_size().into(); - if image_extent.contains(&0) { - return; - } - - let surface_formats = self - .queue - .device() - .physical_device() - .surface_formats(&self.surface, Default::default()) - .unwrap(); - let surface_format = surface_formats[0]; - - let (new_swapchain, new_images) = self - .swapchain - .recreate(SwapchainCreateInfo { - image_extent, - image_usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT, - image_format: surface_format.0, - ..self.swapchain.create_info() - }) - .expect("failed to recreate swapchain"); - - self.swapchain = new_swapchain; - self.attachment_image_views = new_images - .into_iter() - .map(|image| ImageView::new_default(image).unwrap()) - .collect(); - self.viewport.extent = [image_extent[0] as f32, image_extent[1] as f32]; - } -} diff --git a/src/vulkan/resources/buffer.rs b/src/vulkan/resources/buffer.rs deleted file mode 100644 index 318d96e..0000000 --- a/src/vulkan/resources/buffer.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::sync::Arc; -use vulkano::buffer::BufferContents; -use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; -use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}; - -pub struct BufferManager { - memory_allocator: Arc, -} - -impl BufferManager { - pub fn new(memory_allocator: Arc) -> Self { - Self { memory_allocator } - } - - pub fn create_vertex_buffer(&self, data: &[T]) -> Subbuffer<[T]> { - Buffer::from_iter( - self.memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - data.iter().cloned(), - ) - .unwrap() - } - - pub fn create_index_buffer(&self, data: &[u32]) -> Subbuffer<[u32]> { - Buffer::from_iter( - self.memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::INDEX_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - data.iter().cloned(), - ) - .unwrap() - } - - pub fn create_uniform_buffer(&self, data: &T) -> Subbuffer { - Buffer::from_data( - self.memory_allocator.clone(), - BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, - ..Default::default() - }, - AllocationCreateInfo { - memory_type_filter: MemoryTypeFilter::PREFER_DEVICE - | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, - ..Default::default() - }, - *data, - ) - .unwrap() - } -} diff --git a/src/vulkan/resources/descriptor.rs b/src/vulkan/resources/descriptor.rs deleted file mode 100644 index 6b7af8a..0000000 --- a/src/vulkan/resources/descriptor.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::sync::Arc; -use vulkano::descriptor_set::DescriptorSet; -use vulkano::descriptor_set::WriteDescriptorSet; -use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator; -use vulkano::descriptor_set::layout::{ - DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, -}; -use vulkano::device::Device; -use vulkano::shader::ShaderStages; - -pub struct DescriptorManager { - device: Arc, - allocator: Arc, -} - -impl DescriptorManager { - pub fn new(device: Arc, allocator: Arc) -> Self { - Self { device, allocator } - } - - pub fn create_descriptor_set_layout( - &self, - bindings: &[(u32, DescriptorType, u32)], - ) -> Arc { - let mut bindings_map = std::collections::BTreeMap::new(); - for (binding_index, ty, _count) in bindings { - let mut binding = DescriptorSetLayoutBinding::descriptor_type(*ty); - binding.stages = ShaderStages::all_graphics(); - bindings_map.insert(*binding_index, binding); - } - - let create_info = DescriptorSetLayoutCreateInfo { - bindings: bindings_map, - ..Default::default() - }; - - DescriptorSetLayout::new(self.device.clone(), create_info).unwrap() - } - - pub fn create_descriptor_set( - &self, - layout: &Arc, - writes: Vec, - ) -> Arc { - DescriptorSet::new(self.allocator.clone(), layout.clone(), writes, []).unwrap() - } -} diff --git a/src/vulkan/resources/mod.rs b/src/vulkan/resources/mod.rs deleted file mode 100644 index 0c2b51c..0000000 --- a/src/vulkan/resources/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod buffer; -pub mod descriptor; -pub mod vertex;