diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..49879e5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,50 @@ +stages: + - build + - test + - doc + +stable:cargo:build: + stage: build + script: + - rustup toolchain install stable + - rustup default stable + - rustc --version && cargo --version + - cargo build + +stable:cargo:test: + stage: test + dependencies: + - stable:cargo:build + needs: + - stable:cargo:build + script: + - rustup default stable + - rustc --version && cargo --version + - cargo test --jobs 1 + +nightly:cargo:build: + stage: build + script: + - rustup toolchain install nightly + - rustup default nightly + - rustc --version && cargo --version + - cargo build + allow_failure: true + +nightly:cargo:test: + stage: test + dependencies: + - nightly:cargo:build + needs: + - nightly:cargo:build + script: + - rustup default nightly + - rustc --version && cargo --version + - cargo test --jobs 1 + allow_failure: true + +stable:cargo:doc: + stage: doc + needs: [] + script: + - cargo doc diff --git a/Cargo.lock b/Cargo.lock index 87b4b99..2764364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,18 +37,29 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "ash" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69daec0742947f33a85931fa3cb0ce5f07929159dcbd1f0cbb5b2912e2978509" +checksum = "c69a8137596e84c22d57f3da1b5de1d4230b1742a710091c85f4d7ce50f00f38" dependencies = [ "libloading", ] [[package]] -name = "autocfg" -version = "1.0.0" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -81,9 +92,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.50" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" [[package]] name = "cfg-if" @@ -93,9 +104,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.11" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" dependencies = [ "num-integer", "num-traits", @@ -112,27 +123,57 @@ dependencies = [ ] [[package]] -name = "cocoa" -version = "0.20.1" +name = "cmake" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7b6f3f7f4f0b3ec5c5039aaa9e8c3cef97a7a480a400fd62944841314f293d" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" dependencies = [ "bitflags", "block", - "core-foundation", + "core-foundation 0.7.0", "core-graphics", "foreign-types", "libc", "objc", ] +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "core-foundation" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb" +dependencies = [ + "core-foundation-sys 0.8.0", "libc", ] @@ -143,13 +184,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] -name = "core-graphics" -version = "0.19.0" +name = "core-foundation-sys" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd" +checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" + +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e92f5d519093a4178296707dbaa3880eae85a5ef5386675f361a1cf25376e93c" +dependencies = [ + "bitflags", + "core-foundation 0.9.0", "foreign-types", "libc", ] @@ -161,7 +220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" dependencies = [ "cfg-if", - "core-foundation-sys", + "core-foundation-sys 0.7.0", "core-graphics", "libc", "objc", @@ -173,7 +232,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ - "proc-macro2 1.0.18", + "proc-macro2 1.0.20", "quote 1.0.7", "syn", ] @@ -186,18 +245,18 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "dlib" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" dependencies = [ "libloading", ] [[package]] name = "downcast-rs" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "fern" @@ -205,6 +264,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" dependencies = [ + "colored", "log", ] @@ -241,39 +301,55 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "gfx-backend-vulkan" -version = "0.5.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1dee09bd8d8f1ba52c5ba22d1f70c7ffa990c5eb245eb3ef2d0206f631673" +checksum = "a84bda4200a82e1912d575801e2bb76ae19c6256359afbc0adfbbaec02fcadc6" dependencies = [ "arrayvec", "ash", "byteorder", - "core-graphics", + "core-graphics-types", "gfx-hal", + "inplace_it", "lazy_static", "log", "objc", "raw-window-handle", "smallvec", - "winapi 0.3.8", + "winapi 0.3.9", "x11", ] [[package]] name = "gfx-hal" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc96180204064c9493e0fe4a9efeb721e0ac59fe8e1906d0c659142a93114fb1" +checksum = "18d0754f5b7a43915fd7466883b2d1bb0800d7cc4609178d0b27bf143b9e5123" dependencies = [ "bitflags", "raw-window-handle", ] [[package]] -name = "instant" -version = "0.1.2" +name = "hermit-abi" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "inplace_it" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd01a2a73f2f399df96b22dc88ea687ef4d76226284e7531ae3c7ee1dc5cb534" + +[[package]] +name = "instant" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" [[package]] name = "iovec" @@ -293,6 +369,9 @@ dependencies = [ "gfx-backend-vulkan", "gfx-hal", "log", + "num-traits", + "raw-window-handle", + "shaderc", "winit", ] @@ -320,24 +399,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.68" +version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" [[package]] name = "libloading" -version = "0.5.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +checksum = "2443d8f0478b16759158b2f66d525991a05491138bc05814ef52a250148ef4f9" dependencies = [ - "cc", - "winapi 0.3.8", + "cfg-if", + "winapi 0.3.9", ] [[package]] @@ -351,18 +430,18 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] [[package]] name = "log" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -389,14 +468,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -468,13 +547,13 @@ checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d" [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -492,9 +571,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ "autocfg", "num-traits", @@ -502,9 +581,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ "autocfg", ] @@ -526,7 +605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.18", + "proc-macro2 1.0.20", "quote 1.0.7", "syn", ] @@ -542,18 +621,18 @@ dependencies = [ [[package]] name = "ordered-float" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579" dependencies = [ "num-traits", ] [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ "lock_api", "parking_lot_core", @@ -561,16 +640,16 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if", "cloudabi", "libc", "redox_syscall", "smallvec", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -581,15 +660,15 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "proc-macro-crate" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ "toml", ] @@ -605,11 +684,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid 0.2.1", ] [[package]] @@ -627,7 +706,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.18", + "proc-macro2 1.0.20", ] [[package]] @@ -641,9 +720,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "rusttype" @@ -651,17 +730,16 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" dependencies = [ - "rusttype 0.8.2", + "rusttype 0.8.3", ] [[package]] name = "rusttype" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a911032fb5791ccbeec9f28fdcb9bf0983b81f227bafdfd227c658d0731c8a" +checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" dependencies = [ "approx", - "arrayvec", "ordered-float", "stb_truetype", ] @@ -683,9 +761,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.112" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" + +[[package]] +name = "shaderc" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f0cb8d1f8667fc9c50d5054be830a117af5f9a15f87c66b72bbca0c2fca484" +dependencies = [ + "libc", + "shaderc-sys", +] + +[[package]] +name = "shaderc-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89175f80244b82f882033a81bd188f87307c4c39b2fe8d0f194314f270bdea9" +dependencies = [ + "cmake", + "libc", +] [[package]] name = "slab" @@ -695,9 +793,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.2.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" [[package]] name = "smithay-client-toolkit" @@ -726,24 +824,24 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.31" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" dependencies = [ - "proc-macro2 1.0.18", + "proc-macro2 1.0.20", "quote 1.0.7", - "unicode-xid 0.2.0", + "unicode-xid 0.2.1", ] [[package]] name = "time" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "redox_syscall", - "winapi 0.3.8", + "wasi", + "winapi 0.3.9", ] [[package]] @@ -763,9 +861,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "void" @@ -780,10 +878,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", - "winapi 0.3.8", + "winapi 0.3.9", "winapi-util", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wayland-client" version = "0.23.6" @@ -852,9 +956,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -874,11 +978,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -895,7 +999,7 @@ checksum = "1e4ccbf7ddb6627828eace16cacde80fc6bf4dbb3469f88487262a02cf8e7862" dependencies = [ "bitflags", "cocoa", - "core-foundation", + "core-foundation 0.7.0", "core-graphics", "core-video-sys", "dispatch", @@ -914,7 +1018,7 @@ dependencies = [ "raw-window-handle", "smithay-client-toolkit", "wayland-client", - "winapi 0.3.8", + "winapi 0.3.9", "x11-dl", ] @@ -958,6 +1062,6 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xml-rs" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb76e5c421bbbeb8924c60c030331b345555024d56261dae8f3e786ed817c23" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" diff --git a/Cargo.toml b/Cargo.toml index 31ed5e5..b0f5b39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,14 +7,17 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4.8" -chrono = "0.4.11" -fern = "0.6.0" -gfx-hal = "0.5.0" -winit = "0.22.0" +log = "^0.4.8" +chrono = "^0.4.11" +fern = { version = "^0.6.0", features = ["colored"] } +gfx-hal = "^0.6.0" +winit = "^0.22.0" +raw-window-handle = "^0.3.3" +num-traits = "^0.2.12" +shaderc = "^0.7" [dependencies.gfx-backend-vulkan] -version = "0.5" +version = "^0.6.1" features = ["x11"] [lib] @@ -23,5 +26,5 @@ path = "src/lib.rs" [[bin]] name = "iv" -path = "src/bin/main.rs" +path = "main.rs" diff --git a/doc/architecture b/doc/architecture new file mode 100644 index 0000000..bce308f --- /dev/null +++ b/doc/architecture @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/src/bin/main.rs b/main.rs similarity index 67% rename from src/bin/main.rs rename to main.rs index ebf4501..707e91b 100644 --- a/src/bin/main.rs +++ b/main.rs @@ -1,17 +1,26 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; + use chrono; use ::iv::run; fn setup_logger() -> Result<(), fern::InitError> { + use fern::colors::{Color, ColoredLevelConfig}; + + let colors = ColoredLevelConfig::new() + .info(Color::Green) + .debug(Color::Magenta) + .warn(Color::Yellow) + .error(Color::Red); + fern::Dispatch::new() - .format(|out, message, record| { + .format(move |out, message, record| { out.finish(format_args!( "{}[{}][{}] {}", chrono::Local::now().format("[%H:%M:%S]"), - record.level(), + colors.color(record.level()), record.target(), message )) @@ -23,17 +32,11 @@ fn setup_logger() -> Result<(), fern::InitError> { Ok(()) } - -//pub fn draw_frame(hal : &mut HalState, local : &LocalState) -> Result<(), &'static str> { -// -// hal.draw_clear_frame(local.color()); -//} - pub fn main() -> Result<(), &'static str> { setup_logger().unwrap(); run()?; - //print!("test"); + Ok(()) } diff --git a/src/controller.rs b/src/controller.rs new file mode 100644 index 0000000..df6b70d --- /dev/null +++ b/src/controller.rs @@ -0,0 +1,217 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use crate::{ + io::{Input, Output}, + subengine::SubenginePipeline, +}; + +//--Controller Implementation----------------------------------------------------------------------- +pub struct Controller<'a, I, W, O> +where + I: Input, + W: raw_window_handle::HasRawWindowHandle, + O: Output, +{ + pipelines: Vec>, + mouse_pos: [f32; 2], + color: [f32; 3], +} + +impl Controller<'_, I, W, O> +where + I: Input, + W: raw_window_handle::HasRawWindowHandle, + O: Output, +{ + + pub fn new<'a, Ip>(pipelines: Ip) -> Controller<'a, I, W, O> + where + I: 'a + Input, + W: raw_window_handle::HasRawWindowHandle, + O: 'a + Output, + Ip: IntoIterator>, + { + let pipelines_vec = pipelines + .into_iter() + .map(|pipeline| pipeline) + .collect(); + + Controller { + pipelines: pipelines_vec, + mouse_pos: [0.5, 0.5], + color: [1.0, 1.0, 1.0], + } + } + + pub fn run(&mut self) { + use std::time::Duration; //, SystemTime}; + + use crate::{ + subengine::subengine_controller::SubengineCommand, + io::Key, + utils::Triangle, + }; + + let mut input_keys: Vec = Vec::new(); + + loop { +// let frame_start = SystemTime::now(); + + for pipeline in &mut self.pipelines { + for input in &pipeline.inputs { + match input.borrow().read(Duration::from_millis(1)) { + Ok(key) => input_keys.push(key), + Err(_err) => (), + } + } + for input_key in &input_keys { + match input_key { + Key::MouseMove{x,y} => { + self.mouse_pos = [ + (x/1280.0 * 2.0 - 1.0) as f32, + (y/720.0 * 2.0 - 1.0) as f32, + ]; + self.color = [ + (x/1280.0) as f32, + (y/720.0) as f32, + (((x + y)/2.0)/((1280.0 + 720.0)/2.0)) as f32, + ]; + }, + Key::Close => { + info!("Shutting down IV !"); + + return; + } + _ => (), + }; + } + for subengines in &pipeline.subengines { + for subengine in subengines { + subengine.exec(SubengineCommand::Run); + } + for subengine in subengines { + subengine.wait_for_exec(Duration::from_millis(10)).unwrap(); + } + } + + let triangle = Triangle { + points: [self.mouse_pos, [-0.5, 0.5], [-0.5, -0.5]], + }; + + let colors = [ + [self.color[0], self.color[1], self.color[2]], + [self.color[2], self.color[0], self.color[1]], + [self.color[1], self.color[2], self.color[0]], + ]; + + for (renderer, output) in &mut pipeline.renderers { + // match renderer.draw_clear_frame(output, self.color) { + // Err(err) => warn!("{}", err), + // _ => (), + // } + match renderer.draw_triangle_frame(output, triangle, colors) { + Err(err) => warn!("{}", err), + _ => (), + }; + }; + }; + +// let frame_time = SystemTime::now() +// .duration_since(frame_start) +// .unwrap(); +// +// let sleep_time = Duration::from_secs_f32(1.0/60.0) +// .checked_sub(frame_time) +// .unwrap_or_else(|| { +// info!("Engine overloaded !"); +// Duration::from_secs(0) +// }); +// std::thread::sleep(sleep_time); +// +// info!("FPS : {}", 1.0/frame_time.as_secs_f32());// + sleep_time.as_secs_f32())); + } + } +} +//These tests are disabled because of some stange issue with cargo not waiting for the drop +//functions to execute before executing the next test or something like that... +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use super::*; + + use std::iter; + + use crate::{ + io::WinitWindow, + renderer::Renderer, + utils::Rect, + subengine::{SubengineController, TestSubengine}, + }; + + use gfx_backend_vulkan as vk_back; + + //#[test] + fn test_new() { + use std::cell::RefCell; + + //creating windows + let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap()); + let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap()); + + //creating renderers + let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap(); + let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap(); + + //creating subengines + let (test_subengine1, _test_rx1) = TestSubengine::new("run1"); + let test_controller1 = SubengineController::new(test_subengine1); + let (test_subengine2, _test_rx2) = TestSubengine::new("run2"); + let test_controller2 = SubengineController::new(test_subengine2); + + //preparing data + let inputs = vec![&window1, &window2]; + let subengines = vec![vec![test_controller1, test_controller2]]; + let renderers = vec![(renderer1, &window1), (renderer2, &window2)]; + + //creating pipeline + let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers); + + //creating controller + let _controller = Controller::new(vec![subengine_pipeline]); + } + + //#[test] + fn test_run() { + use std::cell::RefCell; + + //creating windows + let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap()); + let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap()); + + //creating renderers + let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap(); + let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap(); + + //creating subengines + let (test_subengine1, _test_rx1) = TestSubengine::new("run1"); + let test_controller1 = SubengineController::new(test_subengine1); + let (test_subengine2, _test_rx2) = TestSubengine::new("run2"); + let test_controller2 = SubengineController::new(test_subengine2); + + //preparing data + let inputs = vec![&window1, &window2]; + let subengines = vec![vec![test_controller1, test_controller2]]; + let renderers = vec![(renderer1, &window1), (renderer2, &window2)]; + + //creating pipeline + let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers); + + //running controller + let mut controller = Controller::new(vec![subengine_pipeline]); + for _i in 0..10 { + controller.run(); + } + } +} + diff --git a/src/input.rs b/src/input.rs deleted file mode 100644 index bdf7450..0000000 --- a/src/input.rs +++ /dev/null @@ -1,42 +0,0 @@ -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; - -use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, -}; - -#[derive(Debug)] -pub struct Input { - pub close_request: bool, - pub new_frame_size: Option<(f64, f64)>, - pub new_mouse_pos: Option<(f64, f64)>, -} - -impl Input { - - pub fn poll_events_loop(event_loop: &mut EventLoop<()>) -> Self { - let mut input = Input::default(); - event_loop.run(|event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent{window_id: _, event} => match event { - WindowEvent::CloseRequested => (), - _ => (), - } - _ => (), - } - }); - input - } - - pub fn default() -> Self { - Input { - close_request: false, - new_frame_size: None, - new_mouse_pos: None, - } - } -} - diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..5423d86 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,84 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::time::Duration; + +use raw_window_handle::HasRawWindowHandle; + +use crate::utils::Rect; + +pub mod winit_window; +pub use self::winit_window::WinitWindow; + + +//--Output trait------------------------------------------------------------------------------------ +/// A trait for the ability of a type to be used as an output by the engine. +/// +/// The `Output` trait defines functions to be used by different components of the engine. This +/// allows to display with any window manager as long as the trait is defined for it. +/// +/// Types that implement the `Output` trait should manage the underlying window and settings. +/// +/// Implementation : the ID and size stored by the type implementing this trait don't need to be +/// initialized to anything specific as the engine will take care of it. +pub trait Output +where + W: HasRawWindowHandle, +{ + /// Return the ID stored internally. Used to select the right swapchain by the render engine. + /// For internal use only. + fn get_id(&self) -> usize; + + /// Store the given ID internally. For internal use only. + fn set_id(&mut self, id: usize); + + //TODO clean that + /// Give mutable acces to the size of the output. The size is a simple rectangle containing the + /// width and height of the `Output`. + fn size(&mut self) -> &mut Rect; + + /// Give reference acces to the underlying window. This is used by the engine during setup to + /// create the revelant ressources. + fn window(&self) -> &W; +} + +//--Input trait------------------------------------------------------------------------------------- +/// A trait for the ability of a type to be used as an input by the engine +/// +/// The `Input` trait defines functions used by different components of the engine. The allow to +/// read inputs from any window manzger as long as the is defined for it. +/// +/// Types that implement the `Input` trait should manage the EventLoop. This can be done in any way +/// (polling, interrupt, synchronous, asynchronous, ...) depending on how the input should be +/// handled. +pub trait Input { + /// Return the next input available or a ReadError if an error occured of the timeout duration + /// was reached. How inputs are handled depends on the implementation. + //TODO change timeout + fn read(&self, timeout: Duration) -> Result; + + fn write(&self, signal: Signal); +} + +//--Key enum---------------------------------------------------------------------------------------- +#[derive(Debug)] +pub enum Key { + Close, + Closed, + Test, + MouseMove { x: f64, y: f64 }, +} + +//--Signal enum------------------------------------------------------------------------------------- +#[derive(Debug)] +pub enum Signal { + Exit, + Test, +} + +//--ReadError enum---------------------------------------------------------------------------------- +#[derive(Debug)] +pub enum ReadError { + Timeout, +} + diff --git a/src/io/: b/src/io/: new file mode 100644 index 0000000..d0d72d7 --- /dev/null +++ b/src/io/: @@ -0,0 +1,177 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + thread, + sync::mpsc, + time::Duration, +}; + +use winit::{ + dpi::PhysicalSize, + event_loop::{EventLoop, EventLoopProxy}, + window::{WindowBuilder, Window}, +}; + +//TODO fix that +use crate::{ + io::{Output, Input, Key, Signal, ReadError}, + utils::Rect, +}; + +#[derive(Debug)] +pub struct WinitWindow { + pub name: String, + size: Rect, + id: usize, + + receiver: mpsc::Receiver, + window: Window, + event_loop_proxy: EventLoopProxy, +} + +impl Drop for WinitWindow { + fn drop(&mut self) { + use winit::event::Event; + + // kill event_loop + if self.event_loop_proxy.send_event(Signal::Exit).is_err() { + warn!("EventLoop thread is dead before Exit signal"); + } + //while match self.read(Duration::from_millis(1)) { + // Ok(Key::Closed) => false, + // _ => true, + //} {} + } +} + +impl WinitWindow { + pub fn new(title: &str, size: Rect) -> Result { + use winit::platform::unix::EventLoopExtUnix; + + debug!("Creating window"); + let name = title.to_string(); + let id = 0; + + //Since we can't move the EventLoop from one thread to another, we need to create it in the + //right thread and then move the Window back to the main thread instead + let cloned_name = name.clone(); + let (tx, rx) = mpsc::channel(); + let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); + let builder = thread::Builder::new().name(title); + //the EventLoop hijacks the thread so there is no need to join it later... + thread::spawn(move || { + + trace!("Creating Window in EventLoop thread"); + //winit doesn't like us creating the EventLoop in another thread either so we have to + //drop crossplatform compatibility :/ + let event_loop = EventLoop::new_any_thread(); + let window = WindowBuilder::new() + .with_inner_size(PhysicalSize {width: size.w, height: size.h}) + .with_title(cloned_name) + .build(&event_loop).unwrap(); + let event_loop_proxy = event_loop.create_proxy(); + + trace!("Sending Window back to main thread"); + tmp_tx.send((window, event_loop_proxy)).unwrap(); + + event_loop.run(move |event: winit::event::Event<'_, Signal>, _, control_flow| { + use winit::{ + event_loop::ControlFlow, + event::Event, + event, + }; + + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => { + tx.send(Key::Closed).unwrap(); + debug!("Closed EventLoop"); + }, + Event::WindowEvent{window_id: _, event} => match event { + event::WindowEvent::CloseRequested => { + debug!("Close requested"); + tx.send(Key::Close).unwrap(); + }, + event::WindowEvent::CursorMoved{position, ..} => { + tx.send(Key::MouseMove{ + x: position.x, + y: position.y, + }).unwrap(); + }, + _ => (), + }, + Event::UserEvent(signal) => match signal { + Signal::Exit => { + debug!("Stopping input thread..."); + *control_flow = ControlFlow::Exit; + }, + Signal::Test => { + tx.send(Key::Test).unwrap(); + }, + }, + _ => (), + }}) + }); + + let (window, event_loop_proxy) = tmp_rx.recv().unwrap(); + trace!("Received Window in main thread"); + + Ok(Self { + name, + size, + id, + receiver: rx, + window, + event_loop_proxy, + }) + } +} + +impl Output for WinitWindow { + fn get_id(&self) -> usize { self.id } + fn set_id(&mut self, id: usize) { self.id = id; } + + fn size(&mut self) -> &mut Rect { &mut self.size } + + fn window(&self) -> &Window { &self.window } +} + +impl Input for WinitWindow { + fn read(&self, timeout: Duration) -> Result { + + match self.receiver.recv_timeout(timeout) { + Ok(key) => Ok(key), + Err(_) => Err(ReadError::Timeout), + } + } + + fn write(&self, signal: Signal) { + + self.event_loop_proxy.send_event(signal) + .map_err(|_| "Could not send Signal to EventLoop").unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::utils::Rect; + + #[test] + fn test_new_drop() { + let _window1 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + let _window2 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + panic!("test"); + } + + #[test] + fn test_read_write() { + let window = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + window.write(Signal::Test); + let input = window.read(Duration::from_millis(1)).unwrap(); + assert!(matches!(input, Key::Test)); + } +} diff --git a/src/io/winit_window.rs b/src/io/winit_window.rs new file mode 100644 index 0000000..809734d --- /dev/null +++ b/src/io/winit_window.rs @@ -0,0 +1,225 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + thread, + sync::mpsc, + time::Duration, +}; + +use winit::{ + dpi::PhysicalSize, + event_loop::{EventLoop, EventLoopProxy}, + window::{WindowBuilder, Window}, +}; + +use crate::{ + io::{Output, Input, Key, Signal, ReadError}, + utils::Rect, +}; + +#[derive(Debug)] +pub struct WinitWindow { + pub name: String, + size: Rect, + id: usize, + + receiver: mpsc::Receiver, + window: Window, + event_loop_proxy: EventLoopProxy, +} + +impl Drop for WinitWindow { + fn drop(&mut self) { + + // kill event_loop + debug!("Sending kill signal..."); + if self.event_loop_proxy.send_event(Signal::Exit).is_err() { + warn!("EventLoop thread is dead before Exit signal"); + } + trace!("Kill signal sent"); + while match self.read(Duration::from_millis(1)) { + Ok(Key::Closed) => false, + Err(err) => match err { + ReadError::Timeout => false, + //_ => true, + }, + _ => true, + } {} + trace!("Dropped window !"); + } +} + +impl WinitWindow { + pub fn new(title: &str, size: Rect) -> Result { + use winit::platform::unix::EventLoopExtUnix; + + debug!("Creating window"); + let name = title.to_string(); + let id = 0; + + //Since we can't move the EventLoop from one thread to another, we need to create it in the + //right thread and then move the Window back to the main thread instead + let cloned_name = name.clone(); + let (tx, rx) = mpsc::sync_channel(1); + let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); + let builder = thread::Builder::new().name(title.into()); + //the EventLoop hijacks the thread so there is no need to join it later... + //TODO manage errors here + let _ = builder.spawn(move || { + + trace!("Creating Window in EventLoop thread"); + //winit doesn't like us creating the EventLoop in another thread either so we have to + //drop crossplatform compatibility :/ + let event_loop = EventLoop::new_any_thread(); + let window = WindowBuilder::new() + .with_inner_size(PhysicalSize {width: size.w, height: size.h}) + .with_title(cloned_name) + .build(&event_loop).unwrap(); + let event_loop_proxy = event_loop.create_proxy(); + + trace!("Sending Window back to main thread"); + tmp_tx.send((window, event_loop_proxy)).unwrap(); + + event_loop.run(move |event: winit::event::Event<'_, Signal>, _, control_flow| { + use winit::{ + event_loop::ControlFlow, + event::Event, + event, + }; + + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => { + tx.send(Key::Closed).unwrap(); + debug!("Closed EventLoop"); + return; + }, + + Event::WindowEvent{window_id: _, event} => match event { + event::WindowEvent::CloseRequested => { + debug!("Close requested"); + tx.send(Key::Close).unwrap(); + }, + event::WindowEvent::CursorMoved{position, ..} => { + let _ = tx.try_send(Key::MouseMove{ + x: position.x, + y: position.y, + }); + }, + _ => (), + }, + + Event::UserEvent(signal) => match signal { + Signal::Exit => { + debug!("Stopping input thread..."); + *control_flow = ControlFlow::Exit; + }, + Signal::Test => { + tx.send(Key::Test).unwrap(); + }, + }, + + _ => (), + }}) + }); + + let (window, event_loop_proxy) = tmp_rx.recv().unwrap(); + trace!("Received Window in main thread"); + + Ok(Self { + name, + size, + id, + receiver: rx, + window, + event_loop_proxy, + }) + } +} + +impl Output for WinitWindow { + fn get_id(&self) -> usize { self.id } + fn set_id(&mut self, id: usize) { self.id = id; } + + fn size(&mut self) -> &mut Rect { &mut self.size } + + fn window(&self) -> &Window { &self.window } +} + +impl Input for WinitWindow { + fn read(&self, timeout: Duration) -> Result { + + match self.receiver.recv_timeout(timeout) { + Ok(key) => Ok(key), + Err(_) => Err(ReadError::Timeout), + } + } + + fn write(&self, signal: Signal) { + + self.event_loop_proxy.send_event(signal) + .map_err(|_| "Could not send Signal to EventLoop").unwrap(); + } +} + +//These tests are disabled for now because they cause some sort of bug with cargo where it doesn't +//take into account other failed tests, even inn single threaded tests. No idea what's going on... +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use super::*; + + use crate::utils::Rect; + + fn setup_logger() -> Result<(), fern::InitError> { + use fern::colors::{Color, ColoredLevelConfig}; + + let colors = ColoredLevelConfig::new() + .info(Color::Green) + .debug(Color::Magenta) + .warn(Color::Yellow) + .error(Color::Red); + + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "{}[{}][{}] {}", + chrono::Local::now().format("[%H:%M:%S]"), + colors.color(record.level()), + record.target(), + message + )) + }) + .level(log::LevelFilter::Trace) + .chain(std::io::stdout()) + .chain(fern::log_file("output.log")?) + .apply()?; + Ok(()) + } + + //#[test] + fn test_new_drop() { + use std::mem::ManuallyDrop; + + let _ = setup_logger(); + let mut window1 = ManuallyDrop::new(WinitWindow::new("IV", + Rect {w: 1280, h: 720}).unwrap()); + let mut window2 = ManuallyDrop::new(WinitWindow::new("IV 2", + Rect {w: 1280, h: 720}).unwrap()); + + unsafe { + ManuallyDrop::drop(&mut window1); + ManuallyDrop::drop(&mut window2); + } + } + + //#[test] + fn test_read_write() { + let window = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); + window.write(Signal::Test); + let input = window.read(Duration::from_millis(1)).unwrap(); + assert!(matches!(input, Key::Test)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 4086d54..1393313 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,26 +5,29 @@ #[allow(unused_imports)] use log::{debug, error, info, trace, warn}; -use std::{ - sync::mpsc, - thread, - collections::HashMap, - cell::RefCell, -}; +use gfx_backend_vulkan as vk_back; -mod winit_state; -use winit_state::WinitState; +pub mod io; -use winit::{ - event::{Event, WindowEvent}, - event_loop::ControlFlow, -}; +pub mod subengine; +use subengine::SubenginePipeline; + +pub mod controller; +use controller::Controller; + +pub mod utils; +//use utils::Rect; mod renderer; -use renderer::Renderer; +//use renderer::Renderer; -//mod local_state; -//use local_state::LocalState; +//mod controller; +//use controller::Controller; + +//use crate::engine::{ +// EngineController, +// TestEngine, +//}; pub enum Command { NoCommand, @@ -51,101 +54,44 @@ pub enum Input { /// The main function of the library pub fn run() -> Result<(), &'static str> { - let winit_state = WinitState::default(); - let mut renderer = Renderer::new(&winit_state.window)?; - //let local_state = LocalState::default(); + use std::{ + iter, + cell::RefCell, + }; - let (input_tx, input_rx) = mpsc::channel(); - let mut window_senders = HashMap::with_capacity(1); - window_senders.insert(1, input_tx); + use crate::{ + io::WinitWindow, + renderer::Renderer, + utils::Rect, + subengine::{SubengineController, TestSubengine}, + }; - let control_thread = RefCell::new(Some(thread::spawn(move || { - #[allow(unused_imports)] - use log::{debug, error, info, trace, warn}; + //creating windows + let window1 = RefCell::new(WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap()); + //let window2 = RefCell::new(WinitWindow::new("IV 2", Rect {w: 720, h: 480}).unwrap()); - let (cmd_tx, cmd_rx) = mpsc::channel(); - let render_thread = thread::spawn(move || { - #[allow(unused_imports)] - use log::{debug, error, info, trace, warn}; + //creating renderers + let renderer1 = Renderer::::new(&mut iter::once(&window1)).unwrap(); + //let renderer2 = Renderer::::new(&mut iter::once(&window2)).unwrap(); - let mut color = [0.0, 0.0, 0.0, 0.0]; + //creating subengines + let (test_subengine1, _test_rx1) = TestSubengine::new("run1"); + let test_controller1 = SubengineController::new(test_subengine1); + //let (test_subengine2, _test_rx2) = TestSubengine::new("run2"); + //let test_controller2 = SubengineController::new(test_subengine2); - loop { + //preparing data + let inputs = vec![&window1]; + let subengines = vec![vec![test_controller1/*, test_controller2*/]]; + let renderers = vec![(renderer1, &window1)/*, (renderer2, &window2)*/]; - //TODO manage errors - let _ = renderer.draw_clear_frame(color); + //creating pipeline + let subengine_pipeline = SubenginePipeline::new(inputs, subengines, renderers); - match cmd_rx.try_recv().unwrap_or(Command::NoCommand) { - Command::NoCommand => (), - Command::Stop => { - warn!("Stop render thread"); - return; - }, - Command::Color{r, g, b, a} => { - color = [r, g, b, a]; - }, - } - } - }); + //running controller + let mut controller = Controller::new(vec![subengine_pipeline]); + controller.run(); - loop { - match input_rx.recv().unwrap() { - Input::Close => { - cmd_tx.send(Command::Stop).unwrap(); - //TODO stop event_loop - warn!("wait for render thread"); - render_thread.join().unwrap(); - warn!("Stop control thread"); - return; - }, - Input::Mouse{x, y} => { - let pos = Command::Color{ - r: (x/1280.0) as f32, - g: (y/720.0) as f32, - b: ((x/1280.0 + y/720.0)/2.0) as f32, - a: 1.0, - }; - cmd_tx.send(pos).unwrap(); - }, - } - } - }))); - - winit_state.event_loop.run(move |event, _, control_flow| { - #[allow(unused_imports)] - use log::{debug, error, info, trace, warn}; - - *control_flow = ControlFlow::Wait; - - //TODO manage errors - let input_tx = window_senders.get(&1).unwrap(); - match event { - Event::WindowEvent{window_id: _, event} => match event { - WindowEvent::CloseRequested => { - input_tx.send(Input::Close).unwrap(); - let handle = control_thread.replace(None).unwrap(); - warn!("Wait for control thread"); - handle.join().unwrap(); - warn!("Stop input thread"); - *control_flow = ControlFlow::Exit; - }, - WindowEvent::CursorMoved{position, ..} => { - input_tx - .send(Input::Mouse{ - x: position.x, - y: position.y}) - .unwrap(); - }, - _ => (), - } - _ => (), - } - }); + Ok(()) } -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/src/renderer.rs b/src/renderer.rs index a734acb..174a033 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,42 +1,278 @@ -use std::mem::ManuallyDrop; +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; -use gfx_hal::{ - queue::QueueGroup, - Backend, - pso::Rect, +use std::{ + mem::ManuallyDrop, + iter, + cell::RefCell, }; -use gfx_backend_vulkan as vk_back; +use crate::{ + io::Output, + utils::Triangle, + +}; -pub mod init; -pub mod render; +mod gpu; +use self::gpu::Gpu; +mod swap_system; +use self::swap_system::SwapSystem; + +mod pipeline; +use self::pipeline::Pipeline; + +//--Renderer implementation------------------------------------------------------------------------- #[derive(Debug)] -pub struct Renderer { - //items need to communicate with the GPU - instance: ManuallyDrop<::Instance>, - surface: ManuallyDrop<::Surface>, - adapter: ManuallyDrop>, - device: vk_back::Device, - queue_group: ManuallyDrop>, - render_pass: ManuallyDrop<::RenderPass>, - swapchain: ManuallyDrop<::Swapchain>, - extent: gfx_hal::window::Extent2D, - format: gfx_hal::format::Format, - render_area: Rect, - - //items needed to render the images - sems_image_available: Vec<::Semaphore>, - sems_render_finished: Vec<::Semaphore>, - fences: Vec<::Fence>, - image_views: Vec<::ImageView>, - framebuffers: Vec<::Framebuffer>, - - command_pool: ManuallyDrop<::CommandPool>, - command_buffers: Vec<::CommandBuffer>, - - //items needed to keep track of the images - image_count: usize, - current_image: usize, +pub struct Renderer { + instance: ManuallyDrop, + gpu: ManuallyDrop>, + swap_systems: Vec>, + pipelines: Vec>, } +impl Drop for Renderer +where + B: gfx_hal::Backend, +{ + fn drop(&mut self) { + use gfx_hal::{ + Instance, + device::Device, + }; + + debug!("Waiting for device to idle..."); + let _ = self.gpu + .device() + .wait_idle(); + + debug!("Dropping Pipelines..."); + for pipeline in self.pipelines.drain(..) { + pipeline.drop(&mut self.gpu); + } + + info!("Dropping Renderer..."); + unsafe { + debug!("Dropping SwapSystems..."); + for mut swap_system in self.swap_systems.drain(..) { + self.instance.destroy_surface(swap_system.drop(&mut self.gpu)); + } + ManuallyDrop::drop(&mut self.gpu); + ManuallyDrop::drop(&mut self.instance); + } + debug!("Renderer dropped !"); + } +} + +impl Renderer +where + B: gfx_hal::Backend, +{ + pub fn new<'a, W: 'a, O: 'a, I>(outputs: &mut I) -> Result, &'static str> + where + W: raw_window_handle::HasRawWindowHandle, + O: Output, + I: Iterator>, + { + use gfx_hal::Instance; + + info!("Creating renderer..."); + let instance = B::Instance::create("IV", 1) + .map_err(|_| "Could not create instance")?; + let (mut gpu, mut surfaces) = Gpu::new(&instance, outputs) + .map_err(|err| err)?; + let swap_systems = { + surfaces + .drain(..) + .map(|surface| SwapSystem::new(&mut gpu, surface)) + .collect::, &str>>()? + }; + let pipelines = vec!(Pipeline::new(&mut gpu, &swap_systems[0]) //TODO improve that + .map_err(|err| err)?); + + debug!("Renderer created !"); + Ok( Renderer { + instance: ManuallyDrop::new(instance), + gpu: ManuallyDrop::new(gpu), + swap_systems, + pipelines, + }) + } + + pub fn draw_clear_frame(&mut self, output: &RefCell, color: [f32; 4]) + -> Result<(), &'static str> + where + W: raw_window_handle::HasRawWindowHandle, + O: Output, + { + use gfx_hal::{ + window::AcquireError, + device::Device, + queue::Submission, + }; + + let swap_system = &mut self.swap_systems[output.borrow_mut().get_id()]; + + let mut frame = match swap_system.acquire_frame(&self.gpu) { + Ok(frame) => frame, + Err(err) => match err { + AcquireError::NotReady => { + return Err("Frame acquisition failed because all Frames are in use"); + }, + AcquireError::OutOfDate => { + swap_system.recreate(&mut self.gpu)?; + debug!("SwapSystem : {:#?}", swap_system); + return Ok(()); + }, + _ => Err("Could not acquire Frame from SwapSystem")?, + }}; + + trace!("Waiting for Frame..."); + unsafe { + let _ = self.gpu.device() + .wait_for_fence(&frame.fences[0], !0) + .map_err(|_| "Failed to wait for Fence")?; + let _ = self.gpu.device() + .reset_fence(&frame.fences[0]) + .map_err(|_| "Failed to reset fence")?; + } + + trace!("Recording CommandBuffer..."); + unsafe { + use gfx_hal::command::{ + CommandBufferFlags, + SubpassContents, + ClearValue, + ClearColor, + CommandBuffer, + }; + + frame.command_buffer.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + + let clear_value = ClearValue {color: ClearColor{float32: color}}; + + frame.command_buffer.begin_render_pass( + &swap_system.render_pass, + &frame.framebuffer.as_ref().unwrap(), + swap_system.render_area, + iter::once(clear_value), + SubpassContents::Inline, + ); + frame.command_buffer.end_render_pass(); + frame.command_buffer.finish(); + } + + trace!("Submiting to queue..."); + let submission = Submission { + command_buffers: iter::once(&*frame.command_buffer), + wait_semaphores: None, + signal_semaphores: iter::once(&frame.signal_semaphores[0]), + }; + + unsafe { + use gfx_hal::queue::CommandQueue; + + self.gpu.queue_mut().submit(submission, Some(&frame.fences[0])); + } + + let result = swap_system.present_frame(frame, &mut self.gpu); + if result.is_err() { + swap_system.recreate(&mut self.gpu).unwrap(); + } + + Ok(()) + } + + pub fn draw_triangle_frame(&mut self, + output: &RefCell, + triangle: Triangle, + colors: [[f32; 3]; 3]) + -> Result<(), &'static str> + where + W: raw_window_handle::HasRawWindowHandle, + O: Output, + { + use gfx_hal::{ + window::AcquireError, + queue::Submission, + }; + + let swap_system = &mut self.swap_systems[output.borrow_mut().get_id()]; + + let mut frame = match swap_system.acquire_frame(&self.gpu) { + Ok(frame) => frame, + Err(err) => match err { + AcquireError::NotReady => { + return Err("Frame acquisition failed because all Frames are in use"); + }, + AcquireError::OutOfDate => { + swap_system.recreate(&mut self.gpu)?; + debug!("SwapSystem : {:#?}", swap_system); + return Ok(()); + }, + _ => Err("Could not acquire Frame from SwapSystem")?, + }}; + + trace!("Uploading triangle data..."); + let points = triangle.points_flat(); + self.pipelines[0].write_vertex_buffer(&self.gpu, 0, (&points).to_vec())?; //TODO meh + let colors_flat = colors.iter().flatten().copied().collect(); + self.pipelines[0].write_vertex_buffer(&self.gpu, 1, colors_flat)?; + + trace!("Recording CommandBuffer..."); + unsafe { + use gfx_hal::command::{ + CommandBufferFlags, + SubpassContents, + ClearValue, + ClearColor, + CommandBuffer, + }; + + frame.command_buffer.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + + const TRIANGLE_CLEAR: ClearValue = + ClearValue {color : ClearColor{float32 : [0.5, 0.5, 0.5, 1.0]}}; + + frame.command_buffer.begin_render_pass( + &swap_system.render_pass, + &frame.framebuffer.as_ref().unwrap(), + swap_system.render_area, + iter::once(TRIANGLE_CLEAR), + SubpassContents::Inline, + ); + frame.command_buffer.bind_graphics_pipeline(self.pipelines[0].raw_pipeline()); + + // storing const data via the CommandBuffer + //let buffer_ref: &B::Buffer = &self.buffer; + //let buffers: Vec<[_; 1]> = vec![(buffer_ref, 0)].into(); + frame.command_buffer.bind_vertex_buffers(0, self.pipelines[0].raw_vertex_buffers()); + + frame.command_buffer.draw(0..3, 0..1); + frame.command_buffer.end_render_pass(); + frame.command_buffer.finish(); + } + + trace!("Submiting to queue..."); + let submission = Submission { + command_buffers: iter::once(&*frame.command_buffer), + wait_semaphores: None, + signal_semaphores: iter::once(&frame.signal_semaphores[0]), + }; + + unsafe { + use gfx_hal::queue::CommandQueue; + + self.gpu.queue_mut().submit(submission, Some(&frame.fences[0])); + } + + let result = swap_system.present_frame(frame, &mut self.gpu); + if result.is_err() { + swap_system.recreate(&mut self.gpu).unwrap(); + } + + Ok(()) + + } +} + diff --git a/src/renderer/gpu.rs b/src/renderer/gpu.rs new file mode 100644 index 0000000..5284502 --- /dev/null +++ b/src/renderer/gpu.rs @@ -0,0 +1,214 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + mem::ManuallyDrop, + ptr::read, + cell::RefCell, +}; + +use gfx_hal::{ + adapter::Adapter, + queue::QueueGroup, +}; + +use gfx_hal::adapter::Gpu as GfxGpu; + +use crate::io::Output; + +//--Gpu implementation------------------------------------------------------------------------------ +/// A struct managing all things related to a specific GPU +/// +/// The `GPU` struct manages the structs from the gfx_hal[gfx_hal] related to the physical device. +/// It implements constructor and destructors as well as acces functions. This is done to make the +/// usage of the HAL easier and make the higher level code less dependant on changes in it. +#[derive(Debug)] +pub struct Gpu { + adapter: ManuallyDrop>, + device: ManuallyDrop, + queue_group: ManuallyDrop>, + command_pool: ManuallyDrop, +} + +impl Drop for Gpu +where + B: gfx_hal::Backend, +{ + fn drop(&mut self) { + use gfx_hal::device::Device; + + debug!("Dropping Gpu..."); + unsafe { + self.device.destroy_command_pool( + ManuallyDrop::into_inner(read(&mut self.command_pool))); + ManuallyDrop::drop(&mut self.queue_group); + ManuallyDrop::drop(&mut self.device); + ManuallyDrop::drop(&mut self.adapter); + } + trace!("Gpu dropped !"); + } +} + +impl Gpu +where + B: gfx_hal::Backend, +{ + /// Create a new `GPU` based on an `instance` of the gfx_hal[gfx_hal]. + pub fn new<'a, W, O: 'a, I>(instance: &B::Instance, outputs: &mut I) + -> Result<(Gpu, Vec), &'static str> + where + W: raw_window_handle::HasRawWindowHandle, + O: Output, + I: Iterator>, + { + use gfx_hal::{ + queue::family::QueueFamily, + }; + + debug!("Creating Gpu..."); + trace!("Creating Surfaces..."); + let surfaces: Vec<_> = { + use gfx_hal::Instance; + + let mut id = 0; + outputs + .by_ref() + .map(|output| unsafe { + output.borrow_mut().set_id(id); + id += 1; + + instance + .create_surface(output.borrow().window()) + .map_err(|_| "Could not create surface") + }) + .collect::, &str>>()? + }; + + let adapter = { + use gfx_hal::{ + Instance, + window::Surface, + adapter::DeviceType, + }; + + // dry run to print all adapters for debug purposes + trace!("Listing Adapters..."); + let adapters = instance + .enumerate_adapters() + .into_iter(); + debug!("Adapters : {:#?}", adapters); + + // selecting an adapter suitabe for all surfaces used + //TODO improve selection + trace!("Selecting Adapter..."); + let adapter = instance + .enumerate_adapters() + .into_iter() + .filter(|a| { + a.queue_families + .iter() + .any(|qf| + QueueFamily::queue_type(qf).supports_graphics() + && surfaces + .iter() + .all(|surface| + surface.supports_queue_family(&qf) + ))}) + .max_by_key(|adapter| { + match adapter.info.device_type { + DeviceType::DiscreteGpu => 4, + DeviceType::IntegratedGpu => 3, + DeviceType::VirtualGpu => 2, + DeviceType::Other => 1, + DeviceType::Cpu => 0, + }}) + .ok_or("Could not find a graphical adapter")?; + + info!("Selected adapter : {}", adapter.info.name); + + adapter + }; + + let (device, queue_group) = { + // get the previously found QueueFamily index + trace!("Getting QueueFamily index..."); + let queue_family = adapter + .queue_families + .iter() + .find(|qf| + QueueFamily::queue_type(*qf).supports_graphics()) + //&& Surface::supports_queue_family(&surface, &qf)) + .ok_or("Could not find suitable queue_family")?; + + // get the related PhysicalDevice and QueueFamily list + trace!("Get Device and QueueGroups..."); + let GfxGpu {device, queue_groups} = unsafe { + use gfx_hal::{ + adapter::PhysicalDevice, + Features, + }; + + adapter + .physical_device + .open(&[(&queue_family, &[1.0; 1])], Features::empty()) + .map_err(|_| "Could not open physical device")? + }; + + // retrieve the selected QueueFamily + trace!("Selecting QueueGroup..."); + let queue_group = queue_groups + .into_iter() + .find(|qg| qg.family == queue_family.id()) + .ok_or("Could not take ownership of the queue")?; + + // check the QueueGroup + trace!("Checking selected QueueGroup..."); + if queue_group.queues.len() <= 0 { + return Err("The QueueGroup does not have any CommandQueues available"); + }; + + (device, queue_group) + }; + + let command_pool = unsafe { + use gfx_hal::{ + pool::CommandPoolCreateFlags, + device::Device, + }; + + trace!("Creating CommandPool..."); + device + .create_command_pool(queue_group.family, + CommandPoolCreateFlags::RESET_INDIVIDUAL) + .map_err(|_| "Could not create CommandPool")? + }; + + debug!("Gpu created !"); + Ok(( + Gpu { + adapter: ManuallyDrop::new(adapter), + device: ManuallyDrop::new(device), + queue_group: ManuallyDrop::new(queue_group), + command_pool: ManuallyDrop::new(command_pool), + }, + surfaces + )) + } + + pub fn adapter(&self) -> &Adapter { + &self.adapter + } + + pub fn device(&self) -> &B::Device { + &self.device + } + + pub fn command_pool_mut(&mut self) -> &mut B::CommandPool { + &mut self.command_pool + } + + pub fn queue_mut(&mut self) -> &mut B::CommandQueue { + &mut self.queue_group.queues[0] + } +} + diff --git a/src/renderer/init.rs b/src/renderer/init.rs deleted file mode 100644 index 9e5e85c..0000000 --- a/src/renderer/init.rs +++ /dev/null @@ -1,415 +0,0 @@ -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; - -use std::{ - mem::ManuallyDrop, -}; - -use core::ptr::read; - -use gfx_hal::{ - Instance, - device::Device, - Backend, - pool::CommandPool, - pso::Rect, -}; - -use gfx_backend_vulkan as vk_back; - -use winit::window::Window; - -use crate::renderer::Renderer; - -impl core::ops::Drop for Renderer { - - //---------------------------------------------------------------------------------------------- - fn drop(&mut self) { - - let _ = self.device.wait_idle(); - - //destroy all underlying ressources - debug!("Destroying Renderer ressources"); - unsafe { - self.command_pool.free(self.command_buffers.drain(..)); - self.device.destroy_command_pool( - ManuallyDrop::into_inner(read(&mut self.command_pool))); - for buffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(buffer); - } - for view in self.image_views.drain(..) { - self.device.destroy_image_view(view); - } - for fence in self.fences.drain(..) { - self.device.destroy_fence(fence); - } - for sem in self.sems_image_available.drain(..) { - self.device.destroy_semaphore(sem); - } - for sem in self.sems_render_finished.drain(..) { - self.device.destroy_semaphore(sem); - } - self.device.destroy_swapchain(ManuallyDrop::into_inner(read(&mut self.swapchain))); - self.device.destroy_render_pass(ManuallyDrop::into_inner(read(&mut self.render_pass))); - ManuallyDrop::drop(&mut self.queue_group); - ManuallyDrop::drop(&mut self.adapter); - self.instance.destroy_surface(ManuallyDrop::into_inner(read(&mut self.surface))); - ManuallyDrop::drop(&mut self.instance); - } - info!("Renderer ressources destroyed"); - } -} - -impl Renderer { - - //---------------------------------------------------------------------------------------------- - pub fn update_swapchain(&mut self) -> Result<(), &'static str> { - use gfx_hal::window::{ - SwapchainConfig, - Surface, - PresentationSurface, - }; - - debug!("Updating swapchain"); - - //creating new swapchain config - let capabilities = self.surface.capabilities(&self.adapter.physical_device); - let swapchain_config = SwapchainConfig::from_caps(&capabilities, self.format, self.extent); - info!("{:?}", swapchain_config); - - self.device - .wait_idle() - .map_err(|_| "Failed to to wait for device to be idle")?; - - unsafe { - self.surface - .configure_swapchain(&self.device, swapchain_config) - .map_err(|_| "Failed to updtate swapchain")?; - debug!("update succesfull !"); - } - - Ok(()) - } - - //---------------------------------------------------------------------------------------------- - pub fn new(window: &Window) -> Result { - use gfx_hal::adapter::Gpu; - - // create top level - let instance = vk_back::Instance::create("IV", 1) - .map_err(|_| "Could not create instance")?; - - let mut surface = unsafe { - instance - .create_surface(window) - .map_err(|_| "Could not create Surface")? - }; - - let (adapter, device, queue_group) = { - use gfx_hal::{ - window::Surface, - queue::family::QueueFamily, - }; - - // find a adapter with a suitable queue family - // TODO add weight for adapters - let adapter = - Instance::enumerate_adapters(&instance) - .into_iter(); -// .find(|a| { -// a.queue_families -// .iter() -// .any(|qf| -// QueueFamily::queue_type(qf).supports_graphics() -// && Surface::supports_queue_family(&surface, &qf) -// )}) -// .ok_or("Could not find a graphical adapter")?; - - debug!("Adapters : {:#?}", adapter); - - let adapter = - Instance::enumerate_adapters(&instance) - .into_iter() - .find(|a| { - a.queue_families - .iter() - .any(|qf| - QueueFamily::queue_type(qf).supports_graphics() - && Surface::supports_queue_family(&surface, &qf) - )}) - .ok_or("Could not find a graphical adapter")?; - - info!("Selected adapter : {}", adapter.info.name); - - // get the suitable queue family index - let queue_family = adapter - .queue_families - .iter() - .find(|qf| - QueueFamily::queue_type(*qf).supports_graphics() - && Surface::supports_queue_family(&surface, &qf)) - .ok_or("Could not find suitable queue_family")?; - - // get the related physical device and queue family list - let Gpu {device, queue_groups} = unsafe { - use gfx_hal::{ - adapter::PhysicalDevice, - Features, - }; - - adapter - .physical_device - .open(&[(&queue_family, &[1.0; 1])], Features::empty()) - .map_err(|_| "Could not open physical device")? - }; - - // retrieve the selected queue family - let queue_group = queue_groups - .into_iter() - .find(|qg| qg.family == queue_family.id()) - .ok_or("Could not take ownership of the queue")?; - - // check our harvest - if queue_group.queues.len() <= 0 { - return Err("The QueueGroup does not have any CommandQueues available"); - }; - - (adapter, device, queue_group) - }; - - let (swapchain, extent, backbuffer, format, image_count) = { - use gfx_hal::window::{ - SwapchainConfig, - Surface, - }; - - let capabilities = surface.capabilities(&adapter.physical_device); - debug!("{:#?}", capabilities); - - // select optimal presentation mode (vsync, triple buffuring, ...) - let present_mode = { - use gfx_hal::window::PresentMode; - - [PresentMode::MAILBOX, PresentMode::FIFO, - PresentMode::RELAXED, PresentMode::IMMEDIATE] - .iter() - .cloned() - .find(|pm| capabilities.present_modes.contains(*pm)) - .ok_or("No PresentMode found")? - }; - - // select optimal alpha composition -// let composite_alpha_mode = { -// use gfx_hal::window::CompositeAlphaMode; -// -// [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, -// CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] -// .iter() -// .cloned() -// .find(|cam| capabilities.composite_alpha_modes.contains(*cam)) -// .ok_or("No CompositeAlphaMode found")? -// }; - - // select optimal format (sRGB) - let format = { - use gfx_hal::format::{Format, ChannelType}; - - match surface.supported_formats(&adapter.physical_device) { - None => Format::Rgba8Srgb, - Some(formats) => formats - .iter() - .find(|f| f.base_format().1 == ChannelType::Srgb) - .cloned() - .ok_or("Could no find suitabe format")? - }}; - - let extent = capabilities.extents.end().clone(); - - // verify swapchain size - let image_count = { - use gfx_hal::window::PresentMode; - - capabilities.image_count.end() - .min(capabilities.image_count.start() - .max(match present_mode { - PresentMode::MAILBOX => &3, - PresentMode::FIFO => &2, - _ => &1, - })).clone() - }; - debug!("image count : {}", image_count); - -// // a surface support at least one layer -// let image_layers = 1; -// -// // verify surface compatibility -// let image_usage = { -// use gfx_hal::image::Usage; -// -// if capabilities.usage.contains(Usage::COLOR_ATTACHMENT) { -// Ok(Usage::COLOR_ATTACHMENT) -// } else { -// Err("This surface does not support color") -// } -// }?; - - let swapchain_config = SwapchainConfig::from_caps(&capabilities, format, extent); - debug!("{:?}", swapchain_config); - - let (swapchain, backbuffer) = unsafe { - device - .create_swapchain(&mut surface, swapchain_config, None) - .map_err(|_| "Failed to create swapchain and backbuffer")? - }; - - (swapchain, extent, backbuffer, format, image_count as usize) - }; - - // creating semaphores and fences - let (sems_image_available, sems_render_finished, fences) = { - - let mut sems_image_available : Vec<::Semaphore> = vec![]; - let mut sems_render_finished : Vec<::Semaphore> = vec![]; - let mut fences :Vec<::Fence> = vec![]; - - for _ in 0..image_count { - sems_image_available - .push(device - .create_semaphore() - .map_err(|_| "Could not create sempahore")? - ); - sems_render_finished - .push(device - .create_semaphore() - .map_err(|_| "Could not create sempahore")? - ); - fences - .push(device - .create_fence(true) - .map_err(|_| "Could not create fence")? - ); - } - - (sems_image_available, sems_render_finished, fences) - }; - - // creating RenderPass - let render_pass = { - use gfx_hal::{ - pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc}, - image::Layout, - }; - - let color_attachment = Attachment { - format : Some(format), - samples : 1, - ops : AttachmentOps { - load : AttachmentLoadOp::Clear, - store : AttachmentStoreOp::Store, - }, - stencil_ops : AttachmentOps::DONT_CARE, - layouts : (Layout::Undefined..Layout::Present), - }; - - let subpass = SubpassDesc { - colors : &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil : None, - inputs : &[], - resolves : &[], - preserves : &[], - }; - - unsafe { - device.create_render_pass(&[color_attachment], &[subpass], &[]) - .map_err(|_| "Could not create render pass")? - } - }; - - // add ImageView "headers" to images in BackBuffer - let image_views : Vec<_> = { - use gfx_hal::{ - image::{ViewKind, SubresourceRange}, - format::{Swizzle, Aspects}, - }; - - backbuffer - .iter() - .map(|img| unsafe { - device - .create_image_view( - &img, - ViewKind::D2, - format, - Swizzle::NO, - SubresourceRange { - aspects : Aspects::COLOR, - levels : 0..1, - layers : 0..1, - }, - ) - .map_err(|_| "Could not create ImageViews") - }) - .collect::, &str>>()? - }; - - let framebuffers: Vec<_> = { - - image_views - .iter() - .map(|image_view| - unsafe { - device - .create_framebuffer(&render_pass, vec![image_view], extent.to_extent()) - .map_err(|_| "Could not create FrameBuffer") - }, - ) - .collect::, &str>>()? - }; - - let mut command_pool = unsafe { - use gfx_hal::pool::CommandPoolCreateFlags; - - device - .create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) - .map_err(|_| "Could not create CommandPool")? - }; - - let command_buffers: Vec<_> = unsafe { - use gfx_hal::command::Level; - - framebuffers - .iter() - .map(|_| command_pool.allocate_one(Level::Primary)) - .collect() - }; - - info!("HAL successfully initialized"); - - Ok( - Renderer { - instance: ManuallyDrop::new(instance), - surface: ManuallyDrop::new(surface), - adapter: ManuallyDrop::new(adapter), - device: device, - queue_group: ManuallyDrop::new(queue_group), - render_pass: ManuallyDrop::new(render_pass), - swapchain: ManuallyDrop::new(swapchain), - extent: extent, - format: format, - render_area: Rect{x: 0, y: 0, w: 1280, h: 720}, - sems_image_available, - sems_render_finished, - fences, - image_views, - framebuffers, - - command_pool: ManuallyDrop::new(command_pool), - command_buffers, - - image_count, - current_image: 0, - } - ) - } -} - diff --git a/src/renderer/pipeline.rs b/src/renderer/pipeline.rs new file mode 100644 index 0000000..3e82281 --- /dev/null +++ b/src/renderer/pipeline.rs @@ -0,0 +1,306 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::mem::{ManuallyDrop, size_of}; + +use gfx_hal::{ + buffer::SubRange, +}; + +use super::{ + gpu::Gpu, + swap_system::SwapSystem, +}; + +mod attachement; +use self::attachement::Attachement; + +static VERT_SRC: &'static str = include_str!("shaders/base.vert"); +static FRAG_SRC: &'static str = include_str!("shaders/base.frag"); + +//--Pipeline implementation------------------------------------------------------------------------- +#[derive(Debug)] +pub struct Pipeline { + set_layout: Vec, + layout: ManuallyDrop, + gfx_pipeline: ManuallyDrop, + vertex_buffers: Vec>, +} + +impl Pipeline +where + B: gfx_hal::Backend, +{ + pub fn drop(mut self, gpu: &mut Gpu) { + use std::ptr::read; + + use gfx_hal::device::Device; + + debug!("Dropping Pipeline..."); + for buffer in self.vertex_buffers.drain(..) { + buffer.drop(gpu); + } + unsafe { + gpu.device() + .destroy_graphics_pipeline( + ManuallyDrop::into_inner(read(&mut self.gfx_pipeline))); + gpu.device() + .destroy_pipeline_layout( + ManuallyDrop::into_inner(read(&mut self.layout))); + for layout in self.set_layout.drain(..) { + gpu.device().destroy_descriptor_set_layout(layout); + }} + } + + pub fn new(gpu: &mut Gpu, swap_system: &SwapSystem) -> Result, &'static str> { + use gfx_hal::{ + device::Device, + pso::{EntryPoint, Specialization, VertexBufferDesc, VertexInputRate, AttributeDesc, + Element, InputAssemblerDesc, Primitive, PrimitiveAssemblerDesc, Rasterizer, + PolygonMode, Face, FrontFace, State, DepthStencilDesc, BakedStates, Viewport, + DescriptorSetLayoutBinding, ShaderStageFlags + }, + format::Format, + }; + + use std::borrow::Cow; + + debug!("Compiling shaders..."); + let mut compiler = shaderc::Compiler::new().ok_or("shaderc not found")?; + let vertex_compile_artifact = compiler + .compile_into_spirv(VERT_SRC, shaderc::ShaderKind::Vertex, "vertex.vert", + "main", + None) + .map_err(|err| {error!("{}", err); + "Could not compile vertex shader"})?; + let fragment_compile_artifact = compiler + .compile_into_spirv(FRAG_SRC, shaderc::ShaderKind::Fragment, "fragement.frag", + "main", + None) + .map_err(|err| {error!("{}", err); + "Could not compile fragment shader"})?; + + trace!("Creating ShaderModules..."); + let vertex_shader_module = unsafe { + gpu.device() + .create_shader_module(vertex_compile_artifact.as_binary()) + .map_err(|err| {error!("{}", err); + "Could not create vertex shader module"})? + }; + let fragment_shader_module = unsafe { + gpu.device() + .create_shader_module(fragment_compile_artifact.as_binary()) + .map_err(|err| {error!("{}", err); + "Could not create fragment shader module"})? + }; + + trace!("Creating shader set..."); + let (vs_entry, fs_entry) = ( + EntryPoint { + entry: "main", + module: &vertex_shader_module, + specialization: Specialization { + constants: Cow::Borrowed{0: &[]}, + data: Cow::Borrowed{0: &[]}, + }, + }, + EntryPoint { + entry: "main", + module: &fragment_shader_module, + specialization: Specialization { + constants: Cow::Borrowed{0: &[]}, + data: Cow::Borrowed{0: &[]}, + }, + }, + ); + + trace!("Creating PrimitiveAssembler..."); + let buffers: Vec = + vec![ + VertexBufferDesc { + binding: 0, + stride: (size_of::()*2) as u32, + rate: VertexInputRate::Vertex, + }, + VertexBufferDesc { + binding: 1, + stride: (size_of::()*3) as u32, + rate: VertexInputRate::Vertex, + }]; + let attributes: Vec = + vec![ + AttributeDesc { + location: 0, + binding: 0, + element: Element { + format: Format::Rgb32Sfloat, + offset: 0, + }, + }, + AttributeDesc { + location: 1, + binding: 1, + element: Element { + format: Format::Rgb32Sfloat, + offset: 0, + }, + }]; + let input_assembler = InputAssemblerDesc { + primitive: Primitive::TriangleList, //TODO switch to strips + with_adjacency: false, + restart_index: None, + }; + + let primitive_assembler = PrimitiveAssemblerDesc::Vertex { + buffers: &buffers, + attributes: &attributes, + input_assembler, + vertex: vs_entry, + tessellation: None, + geometry: None, + }; + + trace!("Creating Rasterizer..."); + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, //TODO adjut that + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: false, + line_width: State::Static{0: 1.0}, //TODO may need to be changed + }; + + trace!("Configuring color blending..."); + let blender = { + use gfx_hal::pso::{BlendState, BlendOp, Factor, BlendDesc, LogicOp, ColorBlendDesc, + ColorMask}; + + let blend_state = BlendState { + color: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + alpha: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }}; + BlendDesc { + logic_op: Some(LogicOp::Copy), + targets: vec![ + ColorBlendDesc { + mask: ColorMask::ALL, + blend: Some(blend_state), + }]} + }; + + trace!("Configuring depth options..."); + let depth_stencil = DepthStencilDesc { + depth: None, + depth_bounds: false, + stencil: None, + }; + + trace!("Configuring baked-in pipeline states..."); + let baked_states = BakedStates { + viewport: Some(Viewport { + rect: swap_system.render_area.clone(), + depth: (0.0..1.0), + }), + scissor: Some(swap_system.render_area.clone()), + blend_color: None, + depth_bounds: None, + }; + + trace!("Creating PipelineLayout..."); + let set_layout = { + let bindings = Vec::::new(); + let immutable_samplers = Vec::::new(); + unsafe { + vec![gpu.device() + .create_descriptor_set_layout(bindings, immutable_samplers) + .map_err(|_| "Could not create DescriptorSetLayout")? + ]}}; + let layout = { + let push_constants = Vec::<(ShaderStageFlags, std::ops::Range)>::new(); + unsafe { + gpu.device() + .create_pipeline_layout(&set_layout, push_constants) + .map_err(|_| "Could not create PipelineLayout")? + }}; + + debug!("Creating GraphicsPipeline..."); + let gfx_pipeline = { + use gfx_hal::{ + pso::{GraphicsPipelineDesc, PipelineCreationFlags, BasePipeline}, + pass::Subpass, + }; + + //manual deref for ManuallyDrop to not cause troubles + let render_pass_ref: &B::RenderPass = &swap_system.render_pass; + + let pipeline_desc = GraphicsPipelineDesc { + primitive_assembler, + rasterizer, + fragment: Some(fs_entry), + blender, + depth_stencil, + multisampling: None, + baked_states, + layout: &layout, + subpass: Subpass { + index: 0, + main_pass: render_pass_ref, + }, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + }; + + unsafe { + gpu.device() + .create_graphics_pipeline(&pipeline_desc, None) + .map_err(|_| "Could not create GraphicsPipeline")? + }}; + + trace!("Destroying no-longer-needed shader modules..."); + unsafe { + gpu.device() + .destroy_shader_module(vertex_shader_module); + gpu.device() + .destroy_shader_module(fragment_shader_module); + }; + + let vertex_buffers = vec![ + Attachement::new(gpu, (size_of::()*2*3) as u64)?, + Attachement::new(gpu, (size_of::()*3*3) as u64)?, + ]; + + Ok( Pipeline { + set_layout, + layout: ManuallyDrop::new(layout), + gfx_pipeline: ManuallyDrop::new(gfx_pipeline), + vertex_buffers, + }) + } + + pub fn raw_pipeline(&self) -> &B::GraphicsPipeline { + &self.gfx_pipeline + } + + pub fn raw_vertex_buffers(&self) -> Vec<(&B::Buffer, SubRange)> { + + self.vertex_buffers + .iter() + //TODO move SubRange to Attachement ? + .map(|buffer| (buffer.get_buffer(), SubRange {offset: 0, size: None})) + .collect() + } + + pub fn write_vertex_buffer(&mut self, gpu: &Gpu, index: usize, data: Vec) + -> Result<(), &'static str> + { + self.vertex_buffers[index].write_buffer(gpu, data) + } + +} + diff --git a/src/renderer/pipeline/attachement.rs b/src/renderer/pipeline/attachement.rs new file mode 100644 index 0000000..9af3950 --- /dev/null +++ b/src/renderer/pipeline/attachement.rs @@ -0,0 +1,136 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::mem::ManuallyDrop; + +use crate::renderer::gpu::Gpu; + +//--Attachement implementation---------------------------------------------------------------------- +#[derive(Debug)] +pub struct Attachement { + buffer: ManuallyDrop, + memory: ManuallyDrop, + size: u64, +} + +impl Attachement +where + B: gfx_hal::Backend, +{ + pub fn drop(mut self, gpu: &mut Gpu) { + use std::ptr::read; + + use gfx_hal::device::Device; + + debug!("Dropping Attachement..."); + unsafe { + gpu.device() + .free_memory(ManuallyDrop::into_inner(read(&mut self.memory))); + gpu.device() + .destroy_buffer(ManuallyDrop::into_inner(read(&mut self.buffer))); + } + } + + pub fn new(gpu: &mut Gpu, size: u64) -> Result, &'static str> { + use gfx_hal::{ + device::Device, + adapter::PhysicalDevice, + buffer::Usage, + }; + + debug!("Creating attachement..."); + let mut buffer = unsafe { + gpu.device() + .create_buffer(size, Usage::VERTEX) + .map_err(|_| "Could not create buffer")? + }; + + trace!("Creating underlying attachement memory..."); + let (memory, size) = { + use gfx_hal::{ + memory::Properties, + MemoryTypeId, + }; + + let requirements = unsafe { + gpu.device().get_buffer_requirements(&buffer) + }; + let memory_type = gpu.adapter() + .physical_device + .memory_properties() + .memory_types + .iter() + .enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 && + memory_type.properties.contains(Properties::CPU_VISIBLE)}) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or("Could not find a suitable memory type to allocate attachement memory")?; + + ( + unsafe { + gpu.device() + .allocate_memory(memory_type, requirements.size) + .map_err(|_| "Could not allocate buffer memory...")? + }, + size, + )}; + + trace!("Binding memory to buffer..."); + unsafe { + gpu.device() + .bind_buffer_memory(&memory, 0, &mut buffer) + .map_err(|__| "Could not bind memory to buffer")?; + } + + + Ok( Attachement { + buffer: ManuallyDrop::new(buffer), + memory: ManuallyDrop::new(memory), + size, + }) + } + + pub fn get_buffer(&self) -> &B::Buffer { + //manual deref for ManuallyDrop to not cause troubles + &self.buffer + } + + pub fn write_buffer(&self, gpu: &Gpu, data: Vec) -> Result<(), &'static str> + { + use gfx_hal::{ + device::Device, + memory::Segment, + }; + + trace!("writing data to buffer..."); + unsafe { + let mapped_memory = gpu.device() + .map_memory(&self.memory, Segment::ALL) + .map_err(|_| "Could not map buffer memory")?; + + //debug!("data : {:?}", data); + //debug!("before : {:?}", std::slice::from_raw_parts(mapped_memory as *mut f32, + // (self.size/4) as usize)); + + std::ptr::copy_nonoverlapping(data.as_ptr() as *const u8, + mapped_memory, self.size as usize); + + //debug!("after : {:?}", std::slice::from_raw_parts(mapped_memory as *mut f32, + // (self.size/4) as usize)); + + //manual deref for ManuallyDrop to not cause troubles + let memory_ref: &B::Memory = &self.memory; + + gpu.device() + .flush_mapped_memory_ranges(std::iter::once((memory_ref, Segment::ALL))) + .map_err(|_| "Could not flush mapped buffer memory")?; + + gpu.device().unmap_memory(&self.memory); + } + + Ok(()) + } + +} + diff --git a/src/renderer/render.rs b/src/renderer/render.rs deleted file mode 100644 index 86b244c..0000000 --- a/src/renderer/render.rs +++ /dev/null @@ -1,113 +0,0 @@ -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; - -use std::iter; - -use gfx_hal::{ - device::Device, - window::Swapchain, - command::CommandBuffer, -}; - -use crate::renderer::Renderer; - -impl Renderer { - - //---------------------------------------------------------------------------------------------- - pub fn draw_clear_frame(&mut self, color: [f32; 4]) -> Result<(), &'static str> { - use gfx_hal::{ - command::{SubpassContents, ClearValue, ClearColor, CommandBufferFlags}, - queue::{Submission, CommandQueue}, - pso::PipelineStage, - }; - - // get current frame fence - let fence = &self.fences[self.current_image]; - - //wait for current fence in case we are submiting too fast - unsafe { - self.device - .wait_for_fence(fence, !0) - .map_err(|_| "Failed to wait for fence")?; - self.device - .reset_fence(fence) - .map_err(|_| "Failed to reset fence")?; - } - - // get current frame semaphores - let sem_image_available = &self.sems_image_available[self.current_image]; - let sem_render_finished = &self.sems_render_finished[self.current_image]; - - // acquire image id in swapchain - let swp_image_id = unsafe { - match self.swapchain.acquire_image(core::u64::MAX, Some(sem_image_available), None) { - Ok((swp_image_id, suboptimal)) => match suboptimal { - Some(_) => { - return self.update_swapchain() - .map_err(|_| "Could not recreate swpachain"); - }, - None => swp_image_id, - } - Err(_) => { - return self.update_swapchain() - .map_err(|_| "Could not recreate swpachain"); - }, - } - }; - -// debug!("current image : {}", self.current_image); -// debug!("swapchain index : {}", swp_image_id); - - unsafe { - - let command_buffer = &mut self.command_buffers[self.current_image]; - command_buffer.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - let clear_value = ClearValue {color: ClearColor{float32: color}}; - - command_buffer.begin_render_pass( - &self.render_pass, - &self.framebuffers[swp_image_id as usize], - self.render_area, - iter::once(clear_value), - SubpassContents::Inline, - ); - command_buffer.end_render_pass(); - command_buffer.finish(); - } - - let command_buffer = &self.command_buffers[self.current_image]; - - // prepare the submission - let submission = Submission { - command_buffers: iter::once(command_buffer), - wait_semaphores: iter::once((sem_image_available, - PipelineStage::COLOR_ATTACHMENT_OUTPUT)), - signal_semaphores: iter::once(sem_render_finished), - }; - - let mut queue = &mut self.queue_group.queues[0]; - - unsafe { - - queue.submit(submission, Some(fence)); - - let result = self.swapchain.present(&mut queue, swp_image_id, Some(sem_render_finished)) - .map_err(|_| "Failed to present into the swapchain")?; - - if result.is_some() { - self.update_swapchain() - .map_err(|_| "Could not recreate swapchain")?; - } - } - - self.current_image = (self.current_image + 1) % self.image_count; - -// if self.current_image == 0 { -// panic!("Abort"); -// } - - Ok(()) - } -} - diff --git a/src/renderer/shaders/base.frag b/src/renderer/shaders/base.frag new file mode 100644 index 0000000..00c4439 --- /dev/null +++ b/src/renderer/shaders/base.frag @@ -0,0 +1,10 @@ +#version 450 + +layout (location = 0) out vec4 frag_color; +layout (location = 1) in vec3 color; + +void main() +{ + frag_color = vec4(color, 1); +} + diff --git a/src/renderer/shaders/base.vert b/src/renderer/shaders/base.vert new file mode 100644 index 0000000..05ff862 --- /dev/null +++ b/src/renderer/shaders/base.vert @@ -0,0 +1,16 @@ +#version 450 + +layout (location = 0) in vec2 position; +layout (location = 1) in vec3 color; + +layout (location = 0) out gl_PerVertex { + vec4 gl_Position; +}; +layout (location = 1) out vec3 frag_color; + +void main() +{ + gl_Position = vec4(position, 0.0, 1.0); + frag_color = color; +} + diff --git a/src/renderer/swap_system.rs b/src/renderer/swap_system.rs new file mode 100644 index 0000000..5d9abb7 --- /dev/null +++ b/src/renderer/swap_system.rs @@ -0,0 +1,342 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + mem::ManuallyDrop, + ptr::read, + collections::VecDeque, +}; + +use gfx_hal::{ + window::{Extent2D, AcquireError, PresentationSurface, SurfaceCapabilities, SwapchainConfig}, + pso::Rect as GfxRect, + format::Format, +}; + +mod frame; +use frame::Frame; + +use super::gpu::Gpu; + +//--SwapSystem implementation----------------------------------------------------------------------- +#[derive(Debug)] +pub struct SwapSystem { + surface: ManuallyDrop, + format: Format, + extent: Extent2D, + pub render_area: GfxRect, //TODO may not be needed (duplicate of extent) + pub render_pass: ManuallyDrop, //TODO move to Pipeline ? + frames: VecDeque>, + frame_nb: usize, +} + +impl SwapSystem +where + B: gfx_hal::Backend, +{ + pub fn drop(&mut self, gpu: &mut Gpu) -> B::Surface { + use gfx_hal::device::Device; + + trace!("Dropping frames..."); + for mut frame in self.frames.drain(..) { + frame.drop(gpu); + } + unsafe { + gpu.device().destroy_render_pass( + ManuallyDrop::into_inner(read(&mut self.render_pass))); + //render_area extent and format can be dropped automatically + self.surface.unconfigure_swapchain(gpu.device()); + trace!("SwapSystem dropped !"); + ManuallyDrop::take(&mut self.surface) + } + } + + pub fn new(gpu: &mut Gpu, mut surface: B::Surface) + -> Result, &'static str> + { + + debug!("Creating SwapSystem..."); + trace!("Obtaining surface capabilities..."); + let capabilities = { + use gfx_hal::window::Surface; + + surface.capabilities(&gpu.adapter().physical_device) + }; + debug!("Surface capabitlities : {:#?}", capabilities); + + trace!("Selecting image format..."); + let format = { + use gfx_hal::{ + format::ChannelType, + window::Surface, + }; + + match surface.supported_formats(&gpu.adapter().physical_device) { + None => Format::Rgba8Srgb, + Some(formats) => formats + .iter() + .find(|f| f.base_format().1 == ChannelType::Srgb) + .cloned() + .ok_or("Could no find suitable format")? + }}; + + trace!("Getting surface's render area size..."); + let extent = capabilities.current_extent + .unwrap_or(*capabilities.extents.end()) + .clone(); + let render_area = GfxRect{x: 0, y: 0, w: extent.width as i16, h: extent.height as i16}; + + trace!("Generating Swapchain configuration..."); + let (swapchain_config, frame_nb) = + generate_swapchain_config(capabilities, &format, &extent)?; + debug!("Swapchain configuration : {:#?}", swapchain_config); + + trace!("Configuring Swapchain..."); + let _ = unsafe { + surface + .configure_swapchain(gpu.device(), swapchain_config) + .map_err(|_| "Failed to create swapchain and backbuffer")? + }; + + trace!("Creating RenderPass..."); + let render_pass = { + use gfx_hal::{ + pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc}, + image::Layout, + device::Device, + }; + + let color_attachment = Attachment { + format : Some(format), + samples : 1, + ops : AttachmentOps { + load : AttachmentLoadOp::Clear, + store : AttachmentStoreOp::Store, + }, + stencil_ops : AttachmentOps::DONT_CARE, + layouts : (Layout::Undefined..Layout::Present), + }; + + let subpass = SubpassDesc { + colors : &[(0, Layout::ColorAttachmentOptimal)], + depth_stencil : None, + inputs : &[], + resolves : &[], + preserves : &[], + }; + + unsafe { + gpu.device().create_render_pass(&[color_attachment], &[subpass], &[]) + .map_err(|_| "Could not create render pass")? + } + }; + + warn!("Generating hard-coded number of frames ({}) !", frame_nb); + let mut frames = VecDeque::with_capacity(frame_nb); + for _ in 0..frame_nb { + let frame = Frame::new(gpu) + .map_err(|_| "Could not create frame")?; + frames.push_back(frame); + }; + + let frame_nb = frames.len(); + + trace!("Created SwapSystem !"); + Ok( SwapSystem { + surface: ManuallyDrop::new(surface), + format, + extent, + render_area, + render_pass: ManuallyDrop::new(render_pass), + frames, + frame_nb, + }) + } + + pub fn recreate(&mut self, gpu: &mut Gpu) -> Result<(), &'static str> { + + trace!("Obtaining surface capabilities..."); + let capabilities = { + use gfx_hal::window::Surface; + + self.surface.capabilities(&gpu.adapter().physical_device) + }; + debug!("Surface capabitlities : {:#?}", capabilities); + + trace!("Getting surface's render area size..."); + self.extent = capabilities.current_extent + .unwrap_or(*capabilities.extents.end()) + .clone(); + self.render_area = + GfxRect{x: 0, y: 0, w: self.extent.width as i16, h: self.extent.height as i16}; + + trace!("Generating Swapchain configuration..."); + let (swapchain_config, _frame_nb) = //TODO check frame nb change + generate_swapchain_config(capabilities, &self.format, &self.extent)?; + debug!("Swapchain configuration : {:#?}", swapchain_config); + + trace!("Configuring Swapchain..."); + let _ = unsafe { + self.surface + .configure_swapchain(gpu.device(), swapchain_config) + .map_err(|_| "Failed to create swapchain and backbuffer")? + }; + + Ok(()) + } + + pub fn acquire_frame(&mut self, gpu: &Gpu) -> Result, AcquireError> { + + //println!("Frame nb : {}", self.frames.len()); + //TODO frames number diminish sometimes at resize... + //static mut id: i32 = 0; + let mut frame = self.frames.pop_back().unwrap(); + //unsafe { + // id = (id+1)%3; + //} + + trace!("Waiting for Frame..."); + unsafe { + use gfx_hal::device::Device; + + let _ = gpu.device() + .wait_for_fence(&frame.fences[0], !0) + .map_err(|_| "Failed to wait for Fence"); //TODO error + let _ = gpu.device() + .reset_fence(&frame.fences[0]) + .map_err(|_| "Failed to reset Fence"); //TODO error + } + + trace!("Reseting CommandBuffer..."); + unsafe { + use gfx_hal::command::CommandBuffer; + + frame.command_buffer.reset(false); //TODO may be needed at some point... + }; + + //unsafe { + // trace!("Acquiring Frame {}...", id); + //} + trace!("Acquiring Frame..."); + let image = unsafe { + match self.surface.acquire_image(core::u64::MAX) { + Ok((image, suboptimal)) => { + match suboptimal { + Some(_) => return Err(AcquireError::OutOfDate), + None => image, + }}, + Err(err) => return Err(err), + }}; + + trace!("Creating Framebuffer..."); + let framebuffer = unsafe { + use gfx_hal::device::Device; + use std::borrow::Borrow; + + gpu.device() + .create_framebuffer(&self.render_pass, + vec![image.borrow()], + self.extent.clone().to_extent()) + .unwrap() //TODO improve that + }; + + //unsafe { + // use gfx_hal::{device::Device}; + + // gpu.device() + // .set_framebuffer_name(&mut framebuffer, format!("frambuffer {}", id).as_str()); + //} + + frame.link_swapchain_image(gpu, image, framebuffer); + + Ok(frame) + } + + pub fn present_frame(&mut self, mut frame: Frame, gpu: &mut Gpu) + -> Result<(), &'static str> { + use gfx_hal::queue::CommandQueue; + + let image = frame + .unlink_swapchain_image() + .unwrap(); //TODO improve that + + trace!("Presenting Frame..."); + let _ = unsafe { + gpu.queue_mut() + .present(&mut self.surface, image, Some(&frame.signal_semaphores[0])) + .map_err(|_| "Failed to present into the swapchain")?; + }; + + frame.destroy_framebuffer(gpu); + self.frames.push_front(frame); + + Ok(()) + } +} + +fn generate_swapchain_config(capabilities: SurfaceCapabilities, + format: &Format, + extent: &Extent2D) + -> Result<(SwapchainConfig, usize), &'static str> { + + trace!("Generating Swapchain configuration..."); + let present_mode = { + use gfx_hal::window::PresentMode; + + [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, + PresentMode::IMMEDIATE] + .iter() + .cloned() + .find(|pm| capabilities.present_modes.contains(*pm)) + .ok_or("No PresentMode values specified!")? + }; + + let composite_alpha_mode = { + use gfx_hal::window::CompositeAlphaMode; + + [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, + CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] + .iter() + .cloned() + .find(|ca| capabilities.composite_alpha_modes.contains(*ca)) + .ok_or("No CompositeAlpha values specified!")? + }; + + let image_count = { + use std::cmp::{min, max}; + use gfx_hal::window::PresentMode; + + let count = match present_mode { + PresentMode::MAILBOX => 3, + _ => 2, //TODO to be checked + }; + max(capabilities.image_count.start().clone(), + min(capabilities.image_count.end().clone(), count)) + }; + + warn!("Hard-coding number of layers per image !"); + let image_layers = 1; + + let image_usage = { + use gfx_hal::image::Usage; + + if capabilities.usage.contains(Usage::COLOR_ATTACHMENT) { + Usage::COLOR_ATTACHMENT + } else { + Err("The Surface isn't capable of supporting color!")? + }}; + + Ok (( + SwapchainConfig { + present_mode, + composite_alpha_mode, + format: format.clone(), + extent: extent.clone(), + image_count: image_count.clone(), + image_layers, + image_usage, + }, + image_count as usize + )) +} + diff --git a/src/renderer/swap_system/frame.rs b/src/renderer/swap_system/frame.rs new file mode 100644 index 0000000..5d4e0dd --- /dev/null +++ b/src/renderer/swap_system/frame.rs @@ -0,0 +1,132 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + mem::ManuallyDrop, + ptr::read, + iter, +}; + +use gfx_hal::window::{PresentationSurface}; + +use super::super::gpu::Gpu; + +//--Frame implementation---------------------------------------------------------------------------- +#[derive(Debug)] +pub struct Frame { + pub wait_semaphores: Vec, + pub signal_semaphores: Vec, + pub fences: Vec, + pub command_buffer: ManuallyDrop, + image_view: Option<>::SwapchainImage>, + pub framebuffer: Option, +} + +impl Frame +where + B: gfx_hal::Backend, +{ + pub fn drop(&mut self, gpu: &mut Gpu) { + use gfx_hal::{ + device::Device, + pool::CommandPool, + }; + + unsafe { + if self.image_view.is_some() { + warn!("Dropping non-presented frame !"); + } + gpu.command_pool_mut().free( + iter::once(ManuallyDrop::into_inner(read(&mut self.command_buffer)))); + for fence in self.fences.drain(..) { + gpu.device().destroy_fence(fence); + } + for semaphore in self.signal_semaphores.drain(..) { + gpu.device().destroy_semaphore(semaphore); + } + for semaphore in self.wait_semaphores.drain(..) { + gpu.device().destroy_semaphore(semaphore); + } + trace!("Frame Dropped !"); + } + } + + pub fn new(gpu: &mut Gpu) -> Result, &'static str> + { + + debug!("Creating Frame..."); + trace!("Creating Semaphore and Fences..."); + let (wait_semaphores, signal_semaphores, fences) = { + use gfx_hal::device::Device; + + let wait_semaphores = vec!( + gpu.device() + .create_semaphore() + .map_err(|_| "Could not create Semaphore")?); + let signal_semaphores = vec!( + gpu.device() + .create_semaphore() + .map_err(|_| "Could not create Semaphore")?); + let fences = vec!( + gpu.device() + .create_fence(true) + .map_err(|_| "Could not create Fence")?); + + (wait_semaphores, signal_semaphores, fences) + }; + + trace!("Allocating CommandBuffer..."); + let command_buffer = unsafe { + use gfx_hal::{ + command::Level, + pool::CommandPool, + }; + + gpu.command_pool_mut().allocate_one(Level::Primary) + }; + + trace!("Created Frame !"); + Ok( Frame { + wait_semaphores, + signal_semaphores, + fences, + command_buffer: ManuallyDrop::new(command_buffer), + image_view: None, + framebuffer: None, + }) + } + + pub fn link_swapchain_image(&mut self, + _gpu: &Gpu, //TODO clear that when framebuffer issue is fixed + image: >::SwapchainImage, + framebuffer: B::Framebuffer) { + + self.framebuffer = Some(framebuffer); + //match self.framebuffer.replace(framebuffer) { + // Some(old_framebuffer) => { + // trace!("Destroying Framebuffer..."); + // unsafe { gpu.device().destroy_framebuffer(old_framebuffer) }; + // }, + // None => (), + //} + + self.image_view = Some(image); + } + + pub fn unlink_swapchain_image(&mut self) + -> Result<>::SwapchainImage, &'static str> { + + self.image_view + .take() + .ok_or("Can not unlink non-linked Frame !") + } + + pub fn destroy_framebuffer(&mut self, gpu: &Gpu) { + unsafe { + use gfx_hal::device::Device; + + gpu.device().destroy_framebuffer(self.framebuffer.take().unwrap()); + } + } +} + diff --git a/src/subengine.rs b/src/subengine.rs new file mode 100644 index 0000000..12fc610 --- /dev/null +++ b/src/subengine.rs @@ -0,0 +1,22 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +pub mod subengine_controller; +pub use self::subengine_controller::SubengineController; + +pub mod subengine_pipeline; +pub use self::subengine_pipeline::SubenginePipeline; + +pub mod test_subengine; +pub use self::test_subengine::TestSubengine; + + +//--SubEngine trait------------------------------------------------------------------------------------ +/// A trait that should be implemented by all subengines running it the game engine. +/// +/// Allow the [`Controller`] to run custom subengines. +pub trait Subengine { + /// Main function of the subengine. Called byt the [`Controller`] for each frame. + fn run(&self); +} + diff --git a/src/subengine/subengine_controller.rs b/src/subengine/subengine_controller.rs new file mode 100644 index 0000000..d84e87f --- /dev/null +++ b/src/subengine/subengine_controller.rs @@ -0,0 +1,140 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + sync::mpsc::{Sender, Receiver}, + thread::JoinHandle, + mem::ManuallyDrop, + ptr::read, + time::Duration, +}; + +use crate::subengine::Subengine; + +//--SubengineController Implementation-------------------------------------------------------------- +/// Handle to manage an [`Subengine`]. +/// +/// The `SubengineController` runs the [`Engine`] in a separated thread with some control code to +/// allow control from the main thread. SubengineControllers are used by the [`Controller`] to +/// interact with Subengines. +#[derive(Debug)] +pub struct SubengineController { + tx: Sender, + rx: Receiver, + handle: ManuallyDrop>, +} + +impl Drop for SubengineController { + fn drop(&mut self) { + self.exec(SubengineCommand::Stop); + unsafe { let _ = ManuallyDrop::into_inner(read(&mut self.handle)).join(); } + debug!("SubengineController dropped !"); + } +} + +impl SubengineController { + /// Creates a new `SubengineController` from a given [`Engine`]. Since the latter will be moved + /// to a separated thread, it must implement the [`Send`] trait. + pub fn new(engine: E) -> SubengineController + where + E: Subengine + std::marker::Send, + { + use std::{ + thread, + sync::mpsc::channel, + }; + + debug!("Creating SubengineController..."); + let (tx, e_rx) = channel(); + let (e_tx, rx) = channel(); + + let handle = thread::spawn(move || { + trace!("Subengine thread started !"); + loop { + //TODO manage errors + match e_rx.recv().unwrap() { + SubengineCommand::Run => engine.run(), + SubengineCommand::Stop => return, + }; + e_tx.send(SubengineResponse::Done).unwrap(); + } + }); + + SubengineController { + tx, + rx, + handle: ManuallyDrop::new(handle), + } + } + + /// Sends a command to the [`Subengine`]. This is function is not blocking and WILL NOT check if + /// the command was received. + pub fn exec(&self, cmd: SubengineCommand) { + self.tx.send(cmd).unwrap(); + } + + /// Blocking function, waits for the [`Subengine`] to finish executing the last command given. + pub fn wait_for_exec(&self, timeout: Duration) -> Result<(),SubengineWaitError> { + match self.rx.recv_timeout(timeout) { + Err(_) => Err(SubengineWaitError::NoResponse), + Ok(val) => match val { + SubengineResponse::Done => Ok(()), + //_ => Err(SubengineWaitError::BadResponse), + }} + } +} + +//--SubengineCommand enum--------------------------------------------------------------------------- +/// Commands that can be sent to an [`Subengine`] via an `EngineController`. +#[derive(Debug)] +pub enum SubengineCommand { + Run, + Stop, +} + +//--SubengineWaitError enum------------------------------------------------------------------------- +/// Errors that can be returned by the wait_for_exec function. +#[derive(Debug)] +pub enum SubengineWaitError { + NoResponse, + BadResponse, +} + +//--SubengineResponse enum-------------------------------------------------------------------------- +#[derive(Debug)] +enum SubengineResponse { + Done, +} + +//--Tests------------------------------------------------------------------------------------------- +#[cfg(test)] +mod tests { + use super::*; + use crate::subengine::TestSubengine; + + #[test] + fn test_new_drop() { + let (test_subengine, _test_rx) = TestSubengine::new("run"); + let _subengine_controller = SubengineController::new(test_subengine); + } + + #[test] + fn test_exec() { + let (test_subengine, test_rx) = TestSubengine::new("run"); + let subengine_controller = SubengineController::new(test_subengine); + + subengine_controller.exec(SubengineCommand::Run); + let response = test_rx.recv_timeout(Duration::from_millis(10)).unwrap(); + assert_eq!(response, "run"); + } + + #[test] + fn test_wait_for_exec() { + let (test_subengine, _test_rx) = TestSubengine::new("run"); + let subengine_controller = SubengineController::new(test_subengine); + + subengine_controller.exec(SubengineCommand::Run); + subengine_controller.wait_for_exec(Duration::from_millis(10)).unwrap(); + } + +} diff --git a/src/subengine/subengine_pipeline.rs b/src/subengine/subengine_pipeline.rs new file mode 100644 index 0000000..45c26e8 --- /dev/null +++ b/src/subengine/subengine_pipeline.rs @@ -0,0 +1,72 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::{ + cell::RefCell, + marker::PhantomData, +}; + +use gfx_backend_vulkan as vk_back; + +use crate::{ + io::{Input, Output}, + subengine::SubengineController, + renderer::Renderer, +}; + +//--SubenginePipeline Implementation---------------------------------------------------------------- +#[derive(Debug)] +pub struct SubenginePipeline<'a, I, W, O> +where + I: Input, + W: raw_window_handle::HasRawWindowHandle, + O: Output, +{ + pub inputs: Vec<&'a RefCell>, + pub subengines: Vec>, + pub renderers: Vec<(Renderer, &'a RefCell)>, + phantom: PhantomData, //needed because of compiler limitations +} + +impl<'a, I, W, O> SubenginePipeline<'a, I, W, O> +where + I: Input, + W: raw_window_handle::HasRawWindowHandle, + O: Output, +{ + + pub fn new(inputs: Ii, subengines: Is, renderers: Ir) + -> SubenginePipeline<'a, I, W, O> + where + Ii: IntoIterator>, + Iss: IntoIterator, + Is: IntoIterator, + Ir: IntoIterator, &'a RefCell)>, + { + let inputs_vec = inputs + .into_iter() + .map(|input| input) + .collect(); + + let subengines_vecs = subengines + .into_iter() + .map(|subengines_vec| subengines_vec + .into_iter() + .map(|subengine| subengine) + .collect()) + .collect(); + + let renderers_vec = renderers + .into_iter() + .map(|renderer| renderer) + .collect(); + + SubenginePipeline{ + inputs: inputs_vec, + subengines: subengines_vecs, + renderers: renderers_vec, + phantom: PhantomData, + } + } +} + diff --git a/src/subengine/test_subengine.rs b/src/subengine/test_subengine.rs new file mode 100644 index 0000000..ac32da6 --- /dev/null +++ b/src/subengine/test_subengine.rs @@ -0,0 +1,36 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use std::sync::mpsc::{Sender, Receiver, channel}; + +use super::*; + +//--TestSubengine Implementation-------------------------------------------------------------------- +/// [`Subengine`] implementation used for tests. +/// +/// Simply sends back the given [`str`] depending on the command. +#[derive(Debug)] +pub struct TestSubengine { + tx: Sender<&'static str>, + run: &'static str, +} + +impl TestSubengine { + /// Creates a `TestSubengine` with specific [`str`]s to use. + pub fn new(run: &'static str) + -> (TestSubengine, Receiver<&'static str>) { + + let (tx, rx) = channel(); + ( + TestSubengine { tx, run }, + rx + ) + } +} + +impl Subengine for TestSubengine { + fn run(&self) { + self.tx.send(self.run).unwrap(); + } +} + diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..ac9a067 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,24 @@ +#[allow(unused_imports)] +use log::{debug, error, info, trace, warn}; + +use num_traits::Num; + +#[derive(Debug, Copy, Clone)] +pub struct Rect { + pub w: I, + pub h: I, +} + +#[derive(Debug, Copy, Clone)] +pub struct Triangle { + pub points: [[f32; 2]; 3], +} + +impl Triangle { + + pub fn points_flat(&self) -> [f32; 6] { + let [[a, b], [c, d], [e, f]] = self.points; + [a, b, c, d, e, f] + } +} + diff --git a/src/winit_state.rs b/src/winit_state.rs deleted file mode 100644 index b8faa77..0000000 --- a/src/winit_state.rs +++ /dev/null @@ -1,49 +0,0 @@ -#[allow(unused_imports)] -use log::{debug, error, info, trace, warn}; - -use winit::{ - dpi::PhysicalSize, - event_loop::{EventLoop}, - window::{WindowBuilder, Window}, - error::OsError, -}; - -#[derive(Debug)] -pub struct WinitState { - pub event_loop: EventLoop<()>, - pub window: Window, -} - -impl WinitState { - - pub fn new (title: &str, size: PhysicalSize) -> Result { - - let event_loop = EventLoop::new(); - - let window = WindowBuilder::new() - .with_inner_size(size) - .with_title(title) - .build(&event_loop) - .unwrap(); - - Ok(Self { - event_loop, - window, - }) - } -} - -impl Default for WinitState { - - fn default() -> WinitState { - - WinitState::new( - "IV", - PhysicalSize { - width : 1280, - height : 720 - } - ).unwrap() - } -} -