Merge branch 'dev' into 'master'
Implemented triangle display See merge request Steins7/iv!16
This commit is contained in:
commit
7f9964b627
50
.gitlab-ci.yml
Normal file
50
.gitlab-ci.yml
Normal file
@ -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
|
||||
306
Cargo.lock
generated
306
Cargo.lock
generated
@ -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"
|
||||
|
||||
17
Cargo.toml
17
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"
|
||||
|
||||
|
||||
1
doc/architecture
Normal file
1
doc/architecture
Normal file
File diff suppressed because one or more lines are too long
@ -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(())
|
||||
}
|
||||
217
src/controller.rs
Normal file
217
src/controller.rs
Normal file
@ -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<W>,
|
||||
{
|
||||
pipelines: Vec<SubenginePipeline<'a, I, W, O>>,
|
||||
mouse_pos: [f32; 2],
|
||||
color: [f32; 3],
|
||||
}
|
||||
|
||||
impl<I, W, O> Controller<'_, I, W, O>
|
||||
where
|
||||
I: Input,
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: Output<W>,
|
||||
{
|
||||
|
||||
pub fn new<'a, Ip>(pipelines: Ip) -> Controller<'a, I, W, O>
|
||||
where
|
||||
I: 'a + Input,
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: 'a + Output<W>,
|
||||
Ip: IntoIterator<Item = SubenginePipeline<'a, I, W, O>>,
|
||||
{
|
||||
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<Key> = 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::<vk_back::Backend>::new(&mut iter::once(&window1)).unwrap();
|
||||
let renderer2 = Renderer::<vk_back::Backend>::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::<vk_back::Backend>::new(&mut iter::once(&window1)).unwrap();
|
||||
let renderer2 = Renderer::<vk_back::Backend>::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
src/input.rs
42
src/input.rs
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
84
src/io.rs
Normal file
84
src/io.rs
Normal file
@ -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<W>
|
||||
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<i32>;
|
||||
|
||||
/// 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<Key, ReadError>;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
177
src/io/:
Normal file
177
src/io/:
Normal file
@ -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<i32>,
|
||||
id: usize,
|
||||
|
||||
receiver: mpsc::Receiver<Key>,
|
||||
window: Window,
|
||||
event_loop_proxy: EventLoopProxy<Signal>,
|
||||
}
|
||||
|
||||
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<i32>) -> Result<WinitWindow, &'static str> {
|
||||
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<Window> 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<i32> { &mut self.size }
|
||||
|
||||
fn window(&self) -> &Window { &self.window }
|
||||
}
|
||||
|
||||
impl Input for WinitWindow {
|
||||
fn read(&self, timeout: Duration) -> Result<Key, ReadError> {
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
225
src/io/winit_window.rs
Normal file
225
src/io/winit_window.rs
Normal file
@ -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<i32>,
|
||||
id: usize,
|
||||
|
||||
receiver: mpsc::Receiver<Key>,
|
||||
window: Window,
|
||||
event_loop_proxy: EventLoopProxy<Signal>,
|
||||
}
|
||||
|
||||
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<i32>) -> Result<WinitWindow, &'static str> {
|
||||
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<Window> 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<i32> { &mut self.size }
|
||||
|
||||
fn window(&self) -> &Window { &self.window }
|
||||
}
|
||||
|
||||
impl Input for WinitWindow {
|
||||
fn read(&self, timeout: Duration) -> Result<Key, ReadError> {
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
152
src/lib.rs
152
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::<vk_back::Backend>::new(&mut iter::once(&window1)).unwrap();
|
||||
//let renderer2 = Renderer::<vk_back::Backend>::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);
|
||||
}
|
||||
}
|
||||
|
||||
304
src/renderer.rs
304
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<<vk_back::Backend as Backend>::Instance>,
|
||||
surface: ManuallyDrop<<vk_back::Backend as Backend>::Surface>,
|
||||
adapter: ManuallyDrop<gfx_hal::adapter::Adapter<vk_back::Backend>>,
|
||||
device: vk_back::Device,
|
||||
queue_group: ManuallyDrop<QueueGroup<vk_back::Backend>>,
|
||||
render_pass: ManuallyDrop<<vk_back::Backend as Backend>::RenderPass>,
|
||||
swapchain: ManuallyDrop<<vk_back::Backend as Backend>::Swapchain>,
|
||||
extent: gfx_hal::window::Extent2D,
|
||||
format: gfx_hal::format::Format,
|
||||
render_area: Rect,
|
||||
|
||||
//items needed to render the images
|
||||
sems_image_available: Vec<<vk_back::Backend as Backend>::Semaphore>,
|
||||
sems_render_finished: Vec<<vk_back::Backend as Backend>::Semaphore>,
|
||||
fences: Vec<<vk_back::Backend as Backend>::Fence>,
|
||||
image_views: Vec<<vk_back::Backend as Backend>::ImageView>,
|
||||
framebuffers: Vec<<vk_back::Backend as Backend>::Framebuffer>,
|
||||
|
||||
command_pool: ManuallyDrop<<vk_back::Backend as Backend>::CommandPool>,
|
||||
command_buffers: Vec<<vk_back::Backend as Backend>::CommandBuffer>,
|
||||
|
||||
//items needed to keep track of the images
|
||||
image_count: usize,
|
||||
current_image: usize,
|
||||
pub struct Renderer<B: gfx_hal::Backend> {
|
||||
instance: ManuallyDrop<B::Instance>,
|
||||
gpu: ManuallyDrop<Gpu<B>>,
|
||||
swap_systems: Vec<SwapSystem<B>>,
|
||||
pipelines: Vec<Pipeline<B>>,
|
||||
}
|
||||
|
||||
impl<B> Drop for Renderer<B>
|
||||
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<B> Renderer<B>
|
||||
where
|
||||
B: gfx_hal::Backend,
|
||||
{
|
||||
pub fn new<'a, W: 'a, O: 'a, I>(outputs: &mut I) -> Result<Renderer<B>, &'static str>
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: Output<W>,
|
||||
I: Iterator<Item = &'a RefCell<O>>,
|
||||
{
|
||||
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::<Result<Vec<_>, &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<W, O>(&mut self, output: &RefCell<O>, color: [f32; 4])
|
||||
-> Result<(), &'static str>
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: Output<W>,
|
||||
{
|
||||
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<W, O>(&mut self,
|
||||
output: &RefCell<O>,
|
||||
triangle: Triangle,
|
||||
colors: [[f32; 3]; 3])
|
||||
-> Result<(), &'static str>
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: Output<W>,
|
||||
{
|
||||
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(())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
214
src/renderer/gpu.rs
Normal file
214
src/renderer/gpu.rs
Normal file
@ -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<B: gfx_hal::Backend> {
|
||||
adapter: ManuallyDrop<Adapter<B>>,
|
||||
device: ManuallyDrop<B::Device>,
|
||||
queue_group: ManuallyDrop<QueueGroup<B>>,
|
||||
command_pool: ManuallyDrop<B::CommandPool>,
|
||||
}
|
||||
|
||||
impl<B> Drop for Gpu<B>
|
||||
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<B> Gpu<B>
|
||||
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<B>, Vec<B::Surface>), &'static str>
|
||||
where
|
||||
W: raw_window_handle::HasRawWindowHandle,
|
||||
O: Output<W>,
|
||||
I: Iterator<Item = &'a RefCell<O>>,
|
||||
{
|
||||
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::<Result<Vec<_>, &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<B> {
|
||||
&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]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Renderer, &'static str> {
|
||||
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<<vk_back::Backend as Backend>::Semaphore> = vec![];
|
||||
let mut sems_render_finished : Vec<<vk_back::Backend as Backend>::Semaphore> = vec![];
|
||||
let mut fences :Vec<<vk_back::Backend as Backend>::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::<Result<Vec<_>, &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::<Result<Vec<_>, &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,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
306
src/renderer/pipeline.rs
Normal file
306
src/renderer/pipeline.rs
Normal file
@ -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<B: gfx_hal::Backend> {
|
||||
set_layout: Vec<B::DescriptorSetLayout>,
|
||||
layout: ManuallyDrop<B::PipelineLayout>,
|
||||
gfx_pipeline: ManuallyDrop<B::GraphicsPipeline>,
|
||||
vertex_buffers: Vec<Attachement<B>>,
|
||||
}
|
||||
|
||||
impl<B> Pipeline<B>
|
||||
where
|
||||
B: gfx_hal::Backend,
|
||||
{
|
||||
pub fn drop(mut self, gpu: &mut Gpu<B>) {
|
||||
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<B>, swap_system: &SwapSystem<B>) -> Result<Pipeline<B>, &'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<VertexBufferDesc> =
|
||||
vec![
|
||||
VertexBufferDesc {
|
||||
binding: 0,
|
||||
stride: (size_of::<f32>()*2) as u32,
|
||||
rate: VertexInputRate::Vertex,
|
||||
},
|
||||
VertexBufferDesc {
|
||||
binding: 1,
|
||||
stride: (size_of::<f32>()*3) as u32,
|
||||
rate: VertexInputRate::Vertex,
|
||||
}];
|
||||
let attributes: Vec<AttributeDesc> =
|
||||
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::<DescriptorSetLayoutBinding>::new();
|
||||
let immutable_samplers = Vec::<B::Sampler>::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<u32>)>::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::<f32>()*2*3) as u64)?,
|
||||
Attachement::new(gpu, (size_of::<f32>()*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<B>, index: usize, data: Vec<f32>)
|
||||
-> Result<(), &'static str>
|
||||
{
|
||||
self.vertex_buffers[index].write_buffer(gpu, data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
136
src/renderer/pipeline/attachement.rs
Normal file
136
src/renderer/pipeline/attachement.rs
Normal file
@ -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<B: gfx_hal::Backend> {
|
||||
buffer: ManuallyDrop<B::Buffer>,
|
||||
memory: ManuallyDrop<B::Memory>,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl<B> Attachement<B>
|
||||
where
|
||||
B: gfx_hal::Backend,
|
||||
{
|
||||
pub fn drop(mut self, gpu: &mut Gpu<B>) {
|
||||
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<B>, size: u64) -> Result<Attachement<B>, &'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<B>, data: Vec<f32>) -> 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(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
10
src/renderer/shaders/base.frag
Normal file
10
src/renderer/shaders/base.frag
Normal file
@ -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);
|
||||
}
|
||||
|
||||
16
src/renderer/shaders/base.vert
Normal file
16
src/renderer/shaders/base.vert
Normal file
@ -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;
|
||||
}
|
||||
|
||||
342
src/renderer/swap_system.rs
Normal file
342
src/renderer/swap_system.rs
Normal file
@ -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<B: gfx_hal::Backend> {
|
||||
surface: ManuallyDrop<B::Surface>,
|
||||
format: Format,
|
||||
extent: Extent2D,
|
||||
pub render_area: GfxRect, //TODO may not be needed (duplicate of extent)
|
||||
pub render_pass: ManuallyDrop<B::RenderPass>, //TODO move to Pipeline ?
|
||||
frames: VecDeque<Frame<B>>,
|
||||
frame_nb: usize,
|
||||
}
|
||||
|
||||
impl<B> SwapSystem<B>
|
||||
where
|
||||
B: gfx_hal::Backend,
|
||||
{
|
||||
pub fn drop(&mut self, gpu: &mut Gpu<B>) -> 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<B>, mut surface: B::Surface)
|
||||
-> Result<SwapSystem<B>, &'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<B>) -> 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<B>) -> Result<Frame<B>, 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<B>, gpu: &mut Gpu<B>)
|
||||
-> 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
|
||||
))
|
||||
}
|
||||
|
||||
132
src/renderer/swap_system/frame.rs
Normal file
132
src/renderer/swap_system/frame.rs
Normal file
@ -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<B: gfx_hal::Backend> {
|
||||
pub wait_semaphores: Vec<B::Semaphore>,
|
||||
pub signal_semaphores: Vec<B::Semaphore>,
|
||||
pub fences: Vec<B::Fence>,
|
||||
pub command_buffer: ManuallyDrop<B::CommandBuffer>,
|
||||
image_view: Option<<B::Surface as PresentationSurface<B>>::SwapchainImage>,
|
||||
pub framebuffer: Option<B::Framebuffer>,
|
||||
}
|
||||
|
||||
impl<B> Frame<B>
|
||||
where
|
||||
B: gfx_hal::Backend,
|
||||
{
|
||||
pub fn drop(&mut self, gpu: &mut Gpu<B>) {
|
||||
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<B>) -> Result<Frame<B>, &'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<B>, //TODO clear that when framebuffer issue is fixed
|
||||
image: <B::Surface as PresentationSurface<B>>::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<<B::Surface as PresentationSurface<B>>::SwapchainImage, &'static str> {
|
||||
|
||||
self.image_view
|
||||
.take()
|
||||
.ok_or("Can not unlink non-linked Frame !")
|
||||
}
|
||||
|
||||
pub fn destroy_framebuffer(&mut self, gpu: &Gpu<B>) {
|
||||
unsafe {
|
||||
use gfx_hal::device::Device;
|
||||
|
||||
gpu.device().destroy_framebuffer(self.framebuffer.take().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
src/subengine.rs
Normal file
22
src/subengine.rs
Normal file
@ -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);
|
||||
}
|
||||
|
||||
140
src/subengine/subengine_controller.rs
Normal file
140
src/subengine/subengine_controller.rs
Normal file
@ -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<SubengineCommand>,
|
||||
rx: Receiver<SubengineResponse>,
|
||||
handle: ManuallyDrop<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
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<E: 'static>(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();
|
||||
}
|
||||
|
||||
}
|
||||
72
src/subengine/subengine_pipeline.rs
Normal file
72
src/subengine/subengine_pipeline.rs
Normal file
@ -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<W>,
|
||||
{
|
||||
pub inputs: Vec<&'a RefCell<I>>,
|
||||
pub subengines: Vec<Vec<SubengineController>>,
|
||||
pub renderers: Vec<(Renderer<vk_back::Backend>, &'a RefCell<O>)>,
|
||||
phantom: PhantomData<W>, //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<W>,
|
||||
{
|
||||
|
||||
pub fn new<Ii, Is, Iss, Ir>(inputs: Ii, subengines: Is, renderers: Ir)
|
||||
-> SubenginePipeline<'a, I, W, O>
|
||||
where
|
||||
Ii: IntoIterator<Item = &'a RefCell<I>>,
|
||||
Iss: IntoIterator<Item = SubengineController>,
|
||||
Is: IntoIterator<Item = Iss>,
|
||||
Ir: IntoIterator<Item = (Renderer<vk_back::Backend>, &'a RefCell<O>)>,
|
||||
{
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/subengine/test_subengine.rs
Normal file
36
src/subengine/test_subengine.rs
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
24
src/utils.rs
Normal file
24
src/utils.rs
Normal file
@ -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<I: Num> {
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<u32>) -> Result<WinitState, OsError> {
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user