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()
- }
-}
-