Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 252db88980 | |||
| 91007853d9 | |||
| 8eec38e55c | |||
| bb7fab4911 | |||
| 14e18cda06 | |||
| f81144918a | |||
| b764bbc83d | |||
| 9d631b686a | |||
| 0a7ebf62ad | |||
| 0560c1897f |
Generated
+345
-246
@@ -39,23 +39,21 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-activity"
|
||||
version = "0.5.2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
|
||||
checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd"
|
||||
dependencies = [
|
||||
"android-properties",
|
||||
"bitflags 2.13.0",
|
||||
"cc",
|
||||
"cesu8",
|
||||
"jni",
|
||||
"jni-sys 0.3.1",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk",
|
||||
"ndk-context",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -139,23 +137,13 @@ version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.3.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
|
||||
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
|
||||
dependencies = [
|
||||
"block-sys",
|
||||
"objc2 0.4.1",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -201,9 +189,9 @@ checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.12.4"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
|
||||
checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"log",
|
||||
@@ -215,9 +203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "calloop-wayland-source"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
|
||||
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
|
||||
dependencies = [
|
||||
"calloop",
|
||||
"rustix 0.38.44",
|
||||
@@ -237,24 +225,12 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
@@ -389,6 +365,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -580,17 +562,6 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "icrate"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
|
||||
dependencies = [
|
||||
"block2 0.3.0",
|
||||
"dispatch",
|
||||
"objc2 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -603,18 +574,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"jni-sys 0.3.1",
|
||||
"jni-macros",
|
||||
"jni-sys 0.4.1",
|
||||
"log",
|
||||
"thiserror 1.0.69",
|
||||
"simd_cesu8",
|
||||
"thiserror 2.0.18",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-macros"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"simd_cesu8",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -775,7 +760,7 @@ dependencies = [
|
||||
"bit-set",
|
||||
"bitflags 2.13.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_aliases",
|
||||
"codespan-reporting",
|
||||
"half",
|
||||
"hashbrown 0.17.1",
|
||||
@@ -805,14 +790,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
|
||||
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"jni-sys 0.3.1",
|
||||
"log",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
"raw-window-handle",
|
||||
"thiserror 1.0.69",
|
||||
@@ -824,15 +809,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.5.0+25.2.9519653"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
|
||||
dependencies = [
|
||||
"jni-sys 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.6.0+11769913"
|
||||
@@ -882,12 +858,12 @@ checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.4.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
|
||||
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
"objc2-encode 3.0.0",
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -896,7 +872,59 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||
dependencies = [
|
||||
"objc2-encode 4.1.0",
|
||||
"objc2-encode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-app-kit"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"libc",
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-data",
|
||||
"objc2-core-image",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-quartz-core 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-contacts"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -923,10 +951,28 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "3.0.0"
|
||||
name = "objc2-core-image"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
|
||||
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-metal 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-contacts",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
@@ -934,6 +980,19 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"dispatch",
|
||||
"libc",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.2"
|
||||
@@ -956,6 +1015,30 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-link-presentation"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.3.2"
|
||||
@@ -965,7 +1048,20 @@ dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.6.2",
|
||||
"objc2 0.6.4",
|
||||
"objc2-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-metal 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -978,8 +1074,63 @@ dependencies = [
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
"objc2-metal",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-metal 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-symbols"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
|
||||
dependencies = [
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data",
|
||||
"objc2-core-image",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-link-presentation",
|
||||
"objc2-quartz-core 0.2.2",
|
||||
"objc2-symbols",
|
||||
"objc2-uniform-type-identifiers",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-uniform-type-identifiers"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
|
||||
dependencies = [
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-core-location",
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1045,6 +1196,26 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
@@ -1079,18 +1250,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.4.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||
checksum = "bc6355899e1c9462875b6757c79f3caa011a1fdae12bbb1a2e72dd1f234f8336"
|
||||
dependencies = [
|
||||
"pollster-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster-macro"
|
||||
version = "0.4.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac5da421106a50887c5b51d20806867db377fbb86bacf478ee0500a912e0c113"
|
||||
checksum = "79af046f3496a2db85e4c2fb04f2e9ff77e6b82f29bd43012b398290ce2c811c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1186,15 +1357,15 @@ checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135"
|
||||
dependencies = [
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-quartz-core 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
@@ -1229,6 +1400,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
@@ -1284,9 +1464,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sctk-adwaita"
|
||||
version = "0.8.3"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7"
|
||||
checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"log",
|
||||
@@ -1295,6 +1475,12 @@ dependencies = [
|
||||
"tiny-skia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -1331,6 +1517,22 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "simd_cesu8"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
"simdutf8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
@@ -1354,9 +1556,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.18.1"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
|
||||
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"calloop",
|
||||
@@ -1522,6 +1724,22 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.25.1"
|
||||
@@ -1676,9 +1894,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.31.2"
|
||||
version = "0.32.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
|
||||
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"wayland-backend",
|
||||
@@ -1688,9 +1906,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-plasma"
|
||||
version = "0.2.0"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
|
||||
checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"wayland-backend",
|
||||
@@ -1701,9 +1919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.2.0"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
|
||||
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"wayland-backend",
|
||||
@@ -1747,9 +1965,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "0.2.4"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -1765,7 +1983,7 @@ dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_aliases",
|
||||
"document-features",
|
||||
"hashbrown 0.17.1",
|
||||
"js-sys",
|
||||
@@ -1796,7 +2014,7 @@ dependencies = [
|
||||
"bit-vec",
|
||||
"bitflags 2.13.0",
|
||||
"bytemuck",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_aliases",
|
||||
"document-features",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
@@ -1860,7 +2078,7 @@ dependencies = [
|
||||
"block2 0.6.2",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_aliases",
|
||||
"glow",
|
||||
"glutin_wgl_sys",
|
||||
"gpu-allocator",
|
||||
@@ -1872,13 +2090,13 @@ dependencies = [
|
||||
"log",
|
||||
"naga",
|
||||
"naga-types",
|
||||
"ndk-sys 0.6.0+11769913",
|
||||
"ndk-sys",
|
||||
"objc2 0.6.4",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
"objc2-metal",
|
||||
"objc2-quartz-core",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-metal 0.3.2",
|
||||
"objc2-quartz-core 0.3.2",
|
||||
"once_cell",
|
||||
"ordered-float",
|
||||
"parking_lot",
|
||||
@@ -2040,20 +2258,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2062,7 +2271,7 @@ version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2074,50 +2283,20 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2129,54 +2308,18 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
@@ -2189,72 +2332,24 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -2263,37 +2358,41 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.29.15"
|
||||
version = "0.30.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
|
||||
checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"android-activity",
|
||||
"atomic-waker",
|
||||
"bitflags 2.13.0",
|
||||
"block2 0.5.1",
|
||||
"bytemuck",
|
||||
"calloop",
|
||||
"cfg_aliases 0.1.1",
|
||||
"cfg_aliases",
|
||||
"concurrent-queue",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"cursor-icon",
|
||||
"icrate",
|
||||
"dpi",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap2",
|
||||
"ndk",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"objc2 0.4.1",
|
||||
"once_cell",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-ui-kit",
|
||||
"orbclient",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"raw-window-handle",
|
||||
"redox_syscall 0.3.5",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix 0.38.44",
|
||||
"sctk-adwaita",
|
||||
"smithay-client-toolkit",
|
||||
"smol_str",
|
||||
"tracing",
|
||||
"unicode-segmentation",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@@ -2303,7 +2402,7 @@ dependencies = [
|
||||
"wayland-protocols-plasma",
|
||||
"web-sys",
|
||||
"web-time",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
"x11-dl",
|
||||
"x11rb",
|
||||
"xkbcommon-dl",
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
WSG is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://crates.io/crates/winit) for simple GPU drawing. It groups the five core wgpu objects (Instance, Surface, Adapter, Device, Queue) behind a single `Context`, adds small building blocks (`Mesh`, `Material`, `PipelineCache`, `Frame`), and exposes the low-level primitives for advanced users.
|
||||
|
||||
> **Status: unstable development version.** The manual workflow below is fully working. The high-level "declarative" workflow and the GPU-driven two-pass pipeline described in the architecture docs are **not implemented yet** — see [Status](#status) and [Roadmap](#roadmap).
|
||||
> **Status: unstable development version.** The manual workflow below is fully working, and the high-level "declarative" workflow (automatic `App` scene rendering) works for flat/NDC drawing. The GPU-driven two-pass pipeline described in the architecture docs is **not implemented yet** — see [Status](#status) and [Roadmap](#roadmap).
|
||||
|
||||
## Status
|
||||
|
||||
| Area | State |
|
||||
|------|-------|
|
||||
| Manual workflow (`Context` + `Renderer` + `PipelineCache`) | ✅ Working |
|
||||
| `App` / `AppBuilder` / `AppHandler` event-loop facade | 🚧 Scaffold — window, events and frame presentation work, but the `render()` callback cannot draw yet (the per-frame view is not exposed to it) |
|
||||
| `Scene` resource/entity registry | 🚧 Registration API works; the engine does not render the scene yet |
|
||||
| `App` / `AppBuilder` / `AppHandler` event-loop facade | ✅ Working — window, events, frame presentation, and **automatic scene rendering** (the per-frame view is exposed via `Frame::view()`) |
|
||||
| `Scene` resource/entity registry | ✅ Working — the engine renders every registered entity automatically in one batched render pass (`App::render_scene`) |
|
||||
| GPU-driven two-pass pipeline (Compute → indirect draw) | 📋 Roadmap — spec in [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) |
|
||||
| 3D transforms (MVP uniforms, camera in the pipeline) | 📋 Roadmap — the bundled shader draws positions straight to NDC today |
|
||||
|
||||
@@ -83,21 +83,19 @@ struct MyGame;
|
||||
|
||||
impl AppHandler for MyGame {
|
||||
// update() has an empty default — implement it to mutate scene state each frame.
|
||||
fn render(&mut self, _app: &mut App) {
|
||||
// The engine acquires and presents the frame around this callback,
|
||||
// but scene rendering is not automated yet — see Roadmap.
|
||||
}
|
||||
// render(app, frame) has a default that draws the whole scene automatically via
|
||||
// app.render_scene(frame.view()). You don't need to implement it for the common case.
|
||||
}
|
||||
|
||||
#[pollster::main]
|
||||
async fn main() -> Result<(), wsg_lib::utils::WsgError> {
|
||||
let app = AppBuilder::new().build().await?;
|
||||
|
||||
// Scene registration is available (string IDs):
|
||||
// Register your scene once (string IDs), then App renders it automatically each frame:
|
||||
// app.cache.register_shader("basic", wsg_lib::utils::BASIC_SHADER_PATH)?;
|
||||
// app.scene.add_mesh("quad", Arc::new(mesh))?;
|
||||
// app.scene.add_material("mat", Arc::new(material))?;
|
||||
// app.scene.add_material("mat", Arc::new(Material::new(app.renderer.format(), "basic", &mut app.cache)))?;
|
||||
// app.scene.add_entity("my_quad", "quad", "mat")?;
|
||||
// ...but the engine will not draw them until the declarative pipeline lands.
|
||||
|
||||
app.run(MyGame)
|
||||
}
|
||||
@@ -109,7 +107,7 @@ async fn main() -> Result<(), wsg_lib::utils::WsgError> {
|
||||
## Architecture overview
|
||||
|
||||
- **Manager layer (`Context`)** — owns the GPU hardware lifecycle (Instance → Surface → Adapter → Device → Queue). Created once at startup; `configure()` sets up the swapchain, `Frame` wraps each frame's surface texture + view.
|
||||
- **Executor layer (`Renderer`)** — binds a `Material` pipeline + `Mesh` buffers into a RenderPass and submits the commands. Today this is one encoder + one submit **per object**.
|
||||
- **Executor layer (`Renderer`)** — binds a `Material` pipeline + `Mesh` buffers into a RenderPass and submits the commands. Rendering a whole `Scene` (`render_scene`) batches all entities into **one encoder + one submit per frame**; the low-level `render` still allocates one per object.
|
||||
- **Supporting pieces** — `PipelineCache` (shader → compiled RenderPipeline, `Arc`-shared), `Material`, `Mesh`/`Vertex`, `Scene` (string-ID registry), `Camera`/`Transform` (types only, not yet used by the pipeline).
|
||||
|
||||
The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass: world matrices + frustum culling → Indirect Draw Buffer, then a single `draw_indexed_indirect` per frame) — is specified in [docs/tech/ARCHI_APP.md](docs/tech/ARCHI_APP.md) and [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) but is **not implemented yet**.
|
||||
@@ -118,11 +116,11 @@ The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass
|
||||
|
||||
| Concept | Type | Responsibility | Status |
|
||||
|---------|------|---------------|--------|
|
||||
| App / AppBuilder | Facade | Window lifecycle + winit event loop + frame presentation | 🚧 Scaffold (no scene rendering) |
|
||||
| AppHandler | Trait | User-defined `update()` / `render()` callbacks | ✅ (render() has no frame access yet) |
|
||||
| Scene | Struct | String-ID registry: meshes, materials, entities | 🚧 Registration only |
|
||||
| App / AppBuilder | Facade | Window lifecycle + winit event loop + frame presentation | ✅ (auto scene rendering via `App::render_scene`) |
|
||||
| AppHandler | Trait | User-defined `update()` / `render()` callbacks | ✅ (`Frame::view()` exposed; default `render` draws the scene) |
|
||||
| Scene | Struct | String-ID registry: meshes, materials, entities | ✅ (registry auto-rendered by the facade) |
|
||||
| Context | Struct | GPU hardware lifecycle (Instance, Surface, Adapter, Device, Queue) | ✅ |
|
||||
| Renderer | Struct | Binds Material + Mesh into a RenderPass, submits | ✅ (one submit per object) |
|
||||
| Renderer | Struct | Binds Material + Mesh into a RenderPass, submits | ✅ (`render_scene` batches one pass/frame) |
|
||||
| PipelineCache | Struct | Shader → compiled RenderPipeline cache | ✅ |
|
||||
| Material | Struct | Shader ID → RenderPipeline | ✅ |
|
||||
| Mesh / Vertex | Struct | GPU geometry container / CPU-side vertex tuple | ✅ |
|
||||
@@ -145,7 +143,7 @@ pollster = "0.4" # only if you use the async AppBuilder
|
||||
| Run the working example | `cargo run -p wsg-lib --example manual` |
|
||||
| Check everything (incl. examples) | `cargo check --all-targets` |
|
||||
|
||||
The `manual` example is the reference for the working, pixel-rendering workflow. The `simple` example (App facade) now compiles and opens a window with a running update → render → present loop, but it does not draw a scene yet (see [Roadmap](#roadmap)).
|
||||
The `manual` example is the reference for the low-level workflow. The `simple` example (App facade) registers a colored quad and renders it automatically through the declarative path — it draws a scene without importing wgpu.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -159,7 +157,7 @@ The architecture docs live in `docs/tech/` and are written in **French**. Each d
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. **Scene auto-rendering** — `App`/`Renderer` iterate registered entities and draw them in one encoder/submit per frame; expose the frame view to `AppHandler::render` for custom draws.
|
||||
1. ✅ **Scene auto-rendering** — `App::render_scene` iterates registered entities and draws them in one encoder/submit per frame; the frame view is exposed to `AppHandler::render` for custom draws. (Done 2026-09-16.)
|
||||
2. **GPU-driven two-pass pipeline** — Compute Pass (world matrices + frustum culling) filling an indirect draw buffer, single `draw_indexed_indirect` (see ARCHI_CPU_GPU).
|
||||
3. **CPU→GPU transform sync** — persistent transform buffers with ring (triple) buffering.
|
||||
4. **Real 3D pipeline** — MVP uniforms + camera support in the vertex shader.
|
||||
|
||||
+103
-130
@@ -1,154 +1,127 @@
|
||||
# DRAFT — Brouillon d'implémentation
|
||||
# DRAFT — Plan d'implémentation : « 3D + éclairage Phong »
|
||||
|
||||
> **Usage.** Ce fichier (dans `docs/`) sert de brouillon pour noter les idées et le plan détaillé de l'étape
|
||||
> en cours. **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est
|
||||
> **Usage.** Ce fichier (dans `docs/`) sert de brouillon pour le plan détaillé de l'étape en cours.
|
||||
> **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est
|
||||
> le code + README.md ; les autres docs `docs/*` restent stables.
|
||||
>
|
||||
> **Étape.** 3D + éclairage Phong (ROADMAP 1.3 + 1.5). Objectif MVP : **un mesh 3D éclairé à l'écran**,
|
||||
> rendu automatiquement par la boucle `App` (Scene auto-render déjà en place).
|
||||
>
|
||||
> **État de départ vérifié.**
|
||||
> - Rendu automatique fonctionnel mais **plat** : `basic_shader.wgsl` pose les positions telles quelles
|
||||
> (`vec4(position, 1.0)`), aucune matrice, aucune uniform, aucun éclairage.
|
||||
> - `Renderer::render_scene` parcourt `iter_entities()` en une passe (`&self`, `&Scene`), sans transform.
|
||||
> - `Scene::entities` : `HashMap<String, (mesh_id, material_id)>` — pas de `Transform` par entité.
|
||||
> - `Camera` (`resources/camera.rs`) : **fichier orphelin, non exporté** (absent de `resources/mod.rs`) ;
|
||||
> `Transform`/`Geometry` exportés via `math`.
|
||||
> - `PipelineCache::build_pipeline` : `bind_group_layouts: &[]`, `immediate_size: 0` — aucun binding.
|
||||
> - Défaut latente : `basic_shader.wgsl` déclare `@location(1) uv`, `(2) color` alors que le
|
||||
> `VertexBufferLayout` réel expose `(1) normal`, `(2) uv`, `(3) color`.
|
||||
|
||||
---
|
||||
|
||||
# Étape : Rendu automatique de la scène + vue de frame exposée
|
||||
## Étape 1 — Fondations data : Transform + Camera exposées
|
||||
|
||||
## 1. Contexte (état réel au 2026-09-16)
|
||||
**But** : donner à chaque entité un `Transform` et rendre `Camera` utilisable via l'API publique, **sans**
|
||||
toucher au rendu (pure façade de données, validable par compilation).
|
||||
|
||||
- `App::run()` : acquiert `Frame` via `context.get_next_frame()`, appelle `handler.render(&mut self)`,
|
||||
puis `renderer.present(frame)`.
|
||||
- ⚠️ `handler.render()` ne reçoit **pas** la frame : le callback ne peut rien dessiner. C'est précisément
|
||||
le point bloquant signalé par `docs/PLAN.md` (§ Phase 2 intégra Scene, check-list) et `docs/ROADMAP.md`
|
||||
(point de départ : « render() ne peut pas encore dessiner — vue de frame non exposée »).
|
||||
- `Renderer::render(view, mesh, material)` existe et fonctionne (usage bas-niveau dans `manual.rs`) :
|
||||
il ouvre 1 encoder + 1 render pass par objet, dessine, soumet.
|
||||
- `Scene` a déjà : `add_mesh`, `add_material`, `add_entity`, `iter_entities() -> (label, mesh, mat)`,
|
||||
`get_mesh`, `get_material`, `remove_entity`.
|
||||
- `Material` porte déjà sa `Arc<RenderPipeline>` (compilée via `PipelineCache`). La scène stocke des
|
||||
`Arc<Material>`. Donc pour dessiner une scène, le code n'a **pas** besoin de consulter le cache :
|
||||
chaque matériau détient sa pipeline. Le « lien PipelineCache → Scene » du PLAN est donc **conceptuel**,
|
||||
pas indispensable côté rendu pour cette étape.
|
||||
- [X] 1.1 **Exporter `Camera`** : dans `lib/src/resources/mod.rs`, ajouter
|
||||
`pub mod camera;` et `pub use camera::Camera;` (aujourd'hui fichier orphelin non compilé). *(fait — 2026-09-16)*
|
||||
- [X] 1.2 **Type `Entity` + transform** : nouvelle struct
|
||||
`Entity { mesh_id: String, material_id: String, transform: Transform }` (module `scene` ou `resources`).
|
||||
Remplacer `Scene::entities: HashMap<String, (String, String)>` par
|
||||
`HashMap<String, Entity>`. Sérialiser `iter_entities()` pour rendre le `&Transform`.
|
||||
*(fait — `lib/src/scene/entity.rs`)*
|
||||
- [X] 1.3 **Compat API** : garder `add_entity(label, mesh_id, material_id)` (transform identité par défaut)
|
||||
+ ajouter `add_entity_with_transform(label, mesh_id, material_id, transform)`.
|
||||
Ajouter `entity_transform(label) -> Option<&Transform>` et `set_entity_transform(label, transform)`.
|
||||
*(fait — 2026-09-16)*
|
||||
- [X] **Validation** : `cargo check --workspace` 0 warning ; `cargo doc --no-deps` 0 warning ; les exemples
|
||||
`simple`/`manual` compilent inchangés (défaut : identité ⇒ même rendu). *(fait — 0 warning. Au passage,
|
||||
`camera.rs` étant désormais compilée, les fonctions glam dépréciées `look_at_rh`/`perspective_rh_gl` ont été
|
||||
migrées vers `glam::camera::rh::view::look_at_mat4` / `glam::camera::rh::proj::opengl::perspective`.)*
|
||||
|
||||
## 2. Objectif
|
||||
## Étape 2 — Shader Phong `standard_shader.wgsl`
|
||||
|
||||
1. Que `AppHandler::render()` reçoive la vue/frame courante.
|
||||
2. Que la scène se rende automatiquement (`app.render(scene)`), sans que `simple.rs` touche à wgpu.
|
||||
3. Que `simple.rs` affiche le quad (4 sommets, 6 indices, matériau `basic`), en gardant ~15 lignes.
|
||||
**But** : produire un rendu 3D éclairé via un nouveau shader, sans encore le brancher.
|
||||
|
||||
## 3. Plan d'implémentation (détail, dans l'ordre)
|
||||
- [ ] 2.1 **Créer `lib/src/shaders/standard_shader.wgsl`** avec le **contrat vertex correct** :
|
||||
`@location(0) position : vec3`, `(1) normal : vec3`, `(2) uv : vec2`, `(3) color : vec4`.
|
||||
- `@group(0) @binding(0)` : `FrameUniforms { view: mat4, proj: mat4, cam_pos: vec4, light_dir: vec4, light_color: vec4 }`
|
||||
- `@group(1) @binding(0)` : `ObjectUniform { model: mat4 }`
|
||||
- `vs_main` : `clip_position = proj * view * model * vec4(position,1)` ; passe `normal`/`color` en espace monde.
|
||||
- `fs_main` : éclairage hémisphérique (ambient) + diffuse directionnel (max(dot(N,L),0)), sortie `vec4(color*light, 1)`.
|
||||
- **Mode unlit** : un flag dans `FrameUniforms` (ou `light_color` nul) **neutralise la directionnelle** → couleur
|
||||
plate. Ainsi « 2D » = `standard` non-éclairé, **cas particulier de la 3D** (décision actée).
|
||||
- [ ] 2.2 **Constantes** : ajouter `STANDARD_SHADER_PATH = "assets/shaders/standard_shader.wgsl"` et
|
||||
`STANDARD_SHADER: &str = include_str!("../shaders/standard_shader.wgsl")` dans `lib/src/utils/conf.rs`.
|
||||
- [ ] 2.3 **Migrer `basic` vers le mode unlit de `standard`** (défaut latente réglée) : plus de pipeline au
|
||||
**layout vide séparé**. Le rendu plat = `standard` non-éclairé (identité/ortho + ambiance) sous le **même
|
||||
layout uniformisé**. Le fallback embarqué (`BASIC_SHADER`) devient la variante unlit de `standard`.
|
||||
- [ ] **Validation** : nouveau `shaders/mod.rs` si include_str le requiert ; `cargo check` OK (le shader n'est
|
||||
pas encore compilé par un pipeline tant que l'Étape 3 ne le charge pas).
|
||||
|
||||
### Étape 3.1 — Exposer la vue de frame au callback
|
||||
## Étape 3 — Infrastructure uniforms dans le `PipelineCache`
|
||||
|
||||
**Fichier** : `lib/src/handler.rs` (+ `app.rs`).
|
||||
**But** : permettre aux pipelines de recevoir des uniforms (bind groups) au lieu de `bind_group_layouts: &[]`.
|
||||
|
||||
Changer la signature :
|
||||
```rust
|
||||
fn render(&mut self, app: &mut App, frame: &Frame);
|
||||
```
|
||||
- `Frame` est un type de bibliothèque (`core::Frame`) qui expose `frame.view()` → `&wgpu::TextureView`.
|
||||
C'est plus riche et plus stable que de passer le `TextureView` brut : on garde une API bibliothèque.
|
||||
- Adapter `handler.rs` docs (consignes `docs/DOCUMENTATION.md` : backticks, description ≤3 lignes,
|
||||
ce que/qui/quand).
|
||||
- [ ] 3.1 **Types bytemuck `Pod`** (nouveau `lib/src/resources/uniform.rs`, ou `math/uniform.rs`) :
|
||||
- `#[repr(C)] #[derive(Pod, Zeroable, Copy, Clone)] FrameUniforms` (voir 2.1)
|
||||
- `#[repr(C)] #[derive(...)] ObjectUniform { model: Mat4 }`
|
||||
- (alignement 16 octets : utiliser `Vec4`/tableaux pour éviter le padding). Exporter via le `mod.rs` concerné.
|
||||
- [ ] 3.2 **Bind group layouts** : dans `build_pipeline`, créer 2 `BindGroupLayout`
|
||||
(frame @0 + object @1, chacun avec un buffer uniform `Vertex`/`Fragment`/`Vertex|Fragment` selon usage) et les
|
||||
passer dans `PipelineLayoutDescriptor.bind_group_layouts`. `immediate_size` reste 0 (pas de `var<immediate>`).
|
||||
- [ ] 3.3 **Acté : un seul layout pour tous** (option A). `build_pipeline` attache **toujours** les 2 bind groups
|
||||
(frame @0 + object @1). Plus de famille `basic` au layout vide : tout matériau partage le même layout
|
||||
uniformisé. `manual`/quad plat migrent (Étape 5).
|
||||
- [ ] **Validation** : `cargo check` 0 warning ; `cargo doc` 0 warning (types documentés, `missing_docs` actif).
|
||||
|
||||
**Fichier** : `lib/src/app.rs`, dans `App::run`, branche `RedrawRequested` :
|
||||
```rust
|
||||
let frame = self.context.get_next_frame();
|
||||
handler.render(&mut self, &frame); // frame est owned (valeur locale) → pas de conflit de borrow avec &mut self
|
||||
self.renderer.present(frame);
|
||||
```
|
||||
> Point d'attention borrow : `frame` est une valeur *owned* détachée de `self.context` une fois acquise,
|
||||
> on peut donc la passer par référence en même temps que `&mut self` sans erreur du borrow checker.
|
||||
## Étape 4 — Rendu 3D dans le `Renderer`
|
||||
|
||||
### Étape 3.2 — Méthode de rendu de scène groupé
|
||||
**But** : `render_scene` applique matrices + éclairage par entité.
|
||||
|
||||
**Fichier** : `lib/src/core/renderer.rs`.
|
||||
- [ ] 4.1 **Buffers frame partagés** : créer le `wgpu::Buffer` `FrameUniforms` + `BindGroup(0)` dans
|
||||
`Renderer::new` (ou à la 1re frame). Écrire chaque frame : view/proj (caméra active) + lumière.
|
||||
- [ ] 4.2 **Buffers object par entité** : `Renderer` maintient un cache
|
||||
`RefCell<HashMap<String, (wgpu::Buffer, wgpu::BindGroup)>>` clefé par label d'entité (créé à la 1re rencontre),
|
||||
car `render_scene(&self, &Scene)` est immuable. Chaque frame : écrire `ObjectUniform.world = entity.transform.to_matrix()` + `set_bind_group(1, ...)`.
|
||||
- [ ] 4.3 **Caméra active** : ajouter `scene.set_active_camera(Camera)` / `scene.active_camera() -> Option<&Camera>`.
|
||||
Calcul du `proj` avec l'aspect de la fenêtre (`window.inner_size()` accessible via `App.window`).
|
||||
- [ ] 4.4 **`draw_entity` étendu** : `set_bind_group(0, frame_bg)` + `set_bind_group(1, object_bg)` avant le draw,
|
||||
pour **tout** matériau (layout unique). Le chemin bas-niveau `Renderer::render` pose aussi les 2 bind groups
|
||||
(frame partagé + object du mesh appelant).
|
||||
- [ ] **Validation** : `cargo check` 0 warning ; exécution `simple` (sans panique, boucle active) ;
|
||||
`manual` non-régressif (chemin bas-niveau).
|
||||
|
||||
Le `Renderer::render(view, mesh, material)` actuel ouvre un encoder+pass **par objet** (N submits par frame
|
||||
si appelé en boucle). Pour rendre une scène entière proprement, ajouter un rendu **batch** :
|
||||
## Étape 5 — Exemple 3D (cube éclairé)
|
||||
|
||||
```rust
|
||||
pub fn render_scene(&self, view: &wgpu::TextureView, scene: &Scene) {
|
||||
let mut encoder = self.device.create_command_encoder(...);
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(/* color attachment: view */);
|
||||
for (_label, mesh, material) in scene.iter_entities() {
|
||||
pass.set_pipeline(&material.pipeline);
|
||||
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||
if let Some(ib) = &mesh.index_buffer {
|
||||
pass.set_index_buffer(ib.slice(..), wgpu::IndexFormat::Uint16);
|
||||
pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
|
||||
} else {
|
||||
pass.draw(0..mesh.num_vertices, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.queue.submit(once(encoder.finish()));
|
||||
}
|
||||
```
|
||||
- **Batching** : un seul pass pour toutes les entités (aligné sur le principe « batching par matériau »
|
||||
évoqué dans renderer.rs / README Phase 4.3). On évite N submits/encoder alloués à la volée.
|
||||
- Conserver `render(view, mesh, material)` (API bas-niveau utilisée par `manual.rs`). Le battle placer du code commun (layout pass / draw d'un mesh) dans un petit helper privé pour éviter la duplication.
|
||||
- Import `crate::scene::Scene`.
|
||||
- Documenter selon `docs/DOCUMENTATION.md`.
|
||||
**But** : démontrer l'objectif MVP à l'écran et **migrer** les exemples sur le pipeline unifié.
|
||||
|
||||
### Étape 3.3 — Automatisation côté App
|
||||
- [ ] 5.1 **Nouvel exemple `lib/examples/cube.rs`** : cube unitaire (positions + normales), matériau
|
||||
`standard` éclairé, `Transform` non-identique, camera + lumière directionnelle, rotation dans `AppHandler::update`.
|
||||
Toujours via `AppBuilder`/scène automatique, **sans importer wgpu** (comme `simple`).
|
||||
- [ ] 5.2 **Migrer `simple.rs`** (quad plat → `standard` **unlit**, transform identité) et **`manual.rs`** (bas niveau
|
||||
→ bind groups frame+object posés, unlit). `basic` disparaît comme famille séparée.
|
||||
- [ ] **Validation** : compile + tourne sans panique ; rotation/éclairage visibles (à confirmer sur GPU/fenêtre).
|
||||
|
||||
**Fichier** : `lib/src/app.rs`.
|
||||
## Étape 6 — Validation globale & docs
|
||||
|
||||
Ajouter sur `App` :
|
||||
```rust
|
||||
pub fn render_scene(&self, view: &wgpu::TextureView) {
|
||||
self.renderer.render_scene(view, &self.scene);
|
||||
}
|
||||
```
|
||||
Le rendu automatique est branché par **défaut** dans le trait (**Option A, décidée**) :
|
||||
```rust
|
||||
fn render(&mut self, app: &mut App, frame: &Frame) {
|
||||
app.render_scene(frame.view());
|
||||
}
|
||||
```
|
||||
→ `simple.rs` **n'implémente même pas `render`** : la scène se rend toute seule, exactement l'esprit
|
||||
« scene auto-render ». L'utilisateur avancé peut surcharger `render` pour contrôler le dessin.
|
||||
- [ ] 6.1 `cargo check --workspace` 0 warning ; `cargo doc --no-deps` 0 warning ; `cargo fmt --all`.
|
||||
- [ ] 6.2 Cas limites (comme à l'étape précédente) : scène vide, mesh non indexé, mesh 0-vertex.
|
||||
- [ ] 6.3 Mettre à jour `README.md` (statut 3D) + `docs/PLAN.md`/`docs/ROADMAP.md` (cases 1.3/1.5 actées).
|
||||
- [ ] 6.4 Commits conventionnels (`feat:`, `docs:`), diffs ciblés.
|
||||
|
||||
### Étape 3.4 — Remplir `simple.rs`
|
||||
---
|
||||
|
||||
**Fichier** : `lib/examples/simple.rs`.
|
||||
## Décisions actées (verrouillées avant l'implémentation)
|
||||
|
||||
- Créer le quad (mêmes 4 sommets + 6 indices que dans `manual.rs`, mais sans toucher à wgpu : tout se fait
|
||||
via `Scene` + `AppBuilder`).
|
||||
- Enregistrer le shader : `app.cache.register_shader("basic", utils::BASIC_SHADER_PATH)`
|
||||
(le shader_id `"basic"` fonctionne déjà en fallback sur `BASIC_SHADER`, cf. `pipeline_cache.rs`).
|
||||
- Créer le matériau avec `Material::new(app.renderer.format(), "basic", &mut app.cache)`.
|
||||
- Créer le mesh avec `Mesh::new(app.renderer.device(), &vertices, Some(&indices))`.
|
||||
- Enregistrer dans `app.scene` : `add_mesh`, `add_material`, `add_entity`.
|
||||
- Tout ce remplissage se fait dans `AppHandler::update()` (ou dans `run()` avant `app.run(...)` — à voir
|
||||
selon où `app` est constructible ; le plus simple : dans `update(&mut self, app)` une fois).
|
||||
|
||||
> ⚠️ Les vertex passent par `Mesh::new(device, ...)` qui exige `wgpu::Device`. **Décision prise** : pour
|
||||
> l'étape 1, utiliser `app.renderer.device()`/`app.renderer.format()` (accès bibliothèque — l'utilisateur
|
||||
> n'importe pas wgpu). Un helper haut niveau `Scene::add_quad_entity` pourra être ajouté plus tard.
|
||||
|
||||
### Étape 3.5 — Validation
|
||||
|
||||
```bash
|
||||
cargo check --workspace
|
||||
cargo doc -p wsg-lib --no-deps # exigence : "generated 0 warnings"
|
||||
cargo run -p wsg-lib --example simple # le quad doit s'afficher
|
||||
cargo run -p wsg-lib --example manual # le workflow manuel doit rester fonctionnel
|
||||
cargo fmt --all
|
||||
```
|
||||
- Vérifier docs (`docs/DOCUMENTATION.md`) : zéro warning, backticks, chaque item public documenté.
|
||||
- Committer proprement (conventional commits, ex. `feat(app): expose frame view and auto-render scene`).
|
||||
|
||||
## 4. Décisions (actées)
|
||||
|
||||
| Sujet | Décision |
|
||||
|-------|----------|
|
||||
| Signature de `render` | Ajouter `&Frame` en paramètre (Option A : default → auto-render) |
|
||||
| Où dessiner la scène | `Renderer::render_scene(view, &Scene)` en **batch** (1 pass unique) |
|
||||
| PipelineCache dans Scene | **Non bougé pour cette étape** : les `Material` portent déjà leur pipeline ; le lien conceptuel cache↔scene est reporté |
|
||||
| wgpu dans `simple.rs` | Via `app.renderer.device()`/`format()` : l'utilisateur n'importe pas wgpu |
|
||||
| Helper quad haut niveau | Reporté (éventuel `Scene::add_quad_entity`) |
|
||||
|
||||
## 5. Notes ouvertes / idées
|
||||
|
||||
- Le "label" d'entité n'est pour l'instant pas utilisé au rendu (juste itéré). OK pour le MVP.
|
||||
- `num_indices == 0` dans le cas non indexé : bien gérer le branchement indexé/non indexé (copié depuis
|
||||
`Renderer::render` actuel).
|
||||
- Après cette étape, l'ajout de lumières/textures/caméras = simple ajout de données à la Scene
|
||||
(voir `docs/ROADMAP.md` Phases 2-4 et `docs/PLAN.md` Phase 4).
|
||||
| Décision | Option proposée | Justification |
|
||||
|----------|-----------------|---------------|
|
||||
| Schéma uniforms | **Acté : 2 bind groups** — frame partagé (@0) + object par entité (@1) | Étendu, portable sur tous backends (Metal/DX12/Vulkan) ; `var<immediate>` neuf, limites de taille et hazard d'écriture par objet ; 2 binds/draw seulement, trivialement « pipeline bind-less » plus tard |
|
||||
| Emplacement types uniforms | **Acté : `resources/uniform.rs`** (`FrameUniforms`, `ObjectUniform`, types `Pod` bytemuck) | Couche de données GPU (avec Camera/Mesh/Material/Vertex) ; préserve `math/` pur (sans bytemuck ni couplage wgpu) |
|
||||
| Cache object buffer | `RefCell<HashMap<label, (Buffer, BindGroup)>>` dans `Renderer` | `render_scene(&self)` immuable ; MVP petit nombre d'entités |
|
||||
| Transform dans l'entité | `Entity { mesh_id, material_id, transform }` + `add_entity_with_transform` | `add_entity` garde sa signature (transform identité) |
|
||||
| Layout pipeline | **Acté : un seul layout pour tous** (frame @0 + object @1) ; `basic` unlit = variante de `standard` | 2D = cas particulier 3D (décision utilisateur) ; supprime la fourchette à deux layouts pour toujours |
|
||||
| Exemple démo | Nouvel exemple `cube.rs` (éclairé) ; `simple.rs` et `manual.rs` **migrés** vers le pipeline unifié (unlit) | Démontre le 3D sans dédoubler ; cohérent avec « un seul layout pour tous » |
|
||||
| Correction `basic_shader.wgsl` | **Supprimer** `basic` comme pipeline séparé ; le quad plat devient `standard` unlit | 2D ⊂ 3D : pas de famille de pipeline dédiée |
|
||||
|
||||
+32
-10
@@ -11,11 +11,11 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
|
||||
|
||||
Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé.
|
||||
|
||||
> **Statut réel (à jour au 2026-09-14).** Ce plan couvre la phase de *consolidation* passée ; la source
|
||||
> de vérité sur l'état actuel est **README.md** et le code. Plusieurs cases `[X]` ci-dessous ont été
|
||||
> re-corrigées car elles ne reflétaient plus la réalité : notamment le rendu de la `Scene` n'est
|
||||
> **pas automatisé** (items Phase 2 et Check-list concernés). Depuis, `simple.rs` a été mis en
|
||||
> conformité (API `AppBuilder`, ~15 lignes, compilation sans importer `winit`/`wgpu`).
|
||||
> **Statut réel (à jour au 2026-09-16).** Ce plan couvre la phase de *consolidation* passée ; la source
|
||||
> de vérité sur l'état actuel est **README.md** et le code. Depuis la révision du 2026-09-14, l'étape
|
||||
> **« Scene auto-render »** a été réalisée : le rendu de la `Scene` est **automatisé** en une seule
|
||||
> passe groupée via `App::render_scene(frame.view())` (appelée par défaut dans `AppHandler::render`),
|
||||
> et `simple.rs` (API `AppBuilder`, sans `winit`/`wgpu`) déclare un quad rendu automatiquement.
|
||||
|
||||
## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue)
|
||||
|
||||
@@ -45,8 +45,12 @@ Une fois la plomberie encapsulée, nous devons rendre l'assemblage des objets co
|
||||
### Intégration de la Scene
|
||||
|
||||
- [X] Formaliser la structure `Scene` : un conteneur qui liste les Entities.
|
||||
- [ ] Associer le `PipelineCache` à la Scene pour que le rendu des matériaux soit automatique (actuellement le cache est porté par `App`, indépendant de la Scene ; le rendu n'est pas automatisé).
|
||||
- [ ] Implémenter la logique `app.render(scene)` : cette méthode doit parcourir la scène, récupérer les matériaux, gérer les pipelines via le cache, et soumettre les draw calls (non implémenté — cf. README, étape 1 du Roadmap : scene auto-rendering).
|
||||
- [ ] Associer le `PipelineCache` à la Scene pour que la gestion des matériaux soit entièrement portée par la scène (actuellement le cache est porté par `App`, indépendant de la Scene — le rendu de la scène est, lui, déjà automatisé depuis 2026-09-16).
|
||||
- [X] Implémenter la logique de rendu de la scène : `App::render_scene(view)` parcourt la scène,
|
||||
récupère les matériaux et soumet tous les draw calls en **une seule passe groupée**
|
||||
(`Renderer::render_scene`), appelée automatiquement chaque frame par l'implémentation par défaut
|
||||
de `AppHandler::render` (Scene auto-render — réalisé 2026-09-16). Reste à brancher : associé au
|
||||
`PipelineCache` porté par la `Scene` (cf. ligne précédente).
|
||||
|
||||
### Gestion des Matériaux et Shaders
|
||||
|
||||
@@ -75,8 +79,26 @@ Une fois les phases 1 à 3 validées, nous pourrons introduire :
|
||||
## Check-list de Vérification pour le LLM d'Assistance
|
||||
|
||||
- [X] Est-ce que `simple.rs` compile sans importer `winit` ou `wgpu` ? (oui — modèle 15 lignes, API `AppBuilder`)
|
||||
- [ ] Est-ce que `App::run` gère bien le cycle update → render → present ? (boucle + présentation OK, mais `render()` ne peut pas encore dessiner — vue de frame non exposée)
|
||||
- [X] Est-ce que `App::run` gère bien le cycle update → render → present ? (oui — la vue de frame est
|
||||
exposée via `Frame::view()`, `render()` dessine la scène automatiquement en une passe via
|
||||
`App::render_scene(frame.view())`, la présentation est faite par `App::run`)
|
||||
- [X] Les modules sont-ils bien exposés via `lib.rs` ?
|
||||
- [X] `pollster` est-il uniquement en dev-dependencies ?
|
||||
- [X] `pollster` est-il uniquement en dev-dependencies ? — **obsolète** : depuis la migration
|
||||
winit 0.30 (2026-09-16), `pollster` est en `dependencies` de la lib (le `block_on` d'init GPU
|
||||
est désormais appelé dans le code de la lib, `app.rs`, cf. note pour mémoire ci-dessous).
|
||||
|
||||
Ce plan garantit que les fondations sont saines. Une fois la Scene rendue automatiquement par `app.render()`, l'ajout de toute nouvelle fonctionnalité (lumières, textures) deviendra une simple question d'ajout de données dans la structure de scène, sans modification de la boucle de rendu.
|
||||
## Note pour mémoire : couplage au runtime async (pollster)
|
||||
|
||||
Depuis la migration winit 0.30, la lib embarque un runtime async pour l'init GPU. Le point de
|
||||
couplage actuel est **unique** : `pollster::block_on(Context::new(...))` dans `lib/src/app.rs`
|
||||
(`resumed()`), plus le macro `#[pollster::main]` dans les exemples (crates séparées, hors lib).
|
||||
|
||||
À ce stade (un seul appel), **on ne crée volontairement PAS d'abstraction** : ce serait du
|
||||
sur-engineering pour un seul point d'appel. Mais si la lib acquiert d'autres appels async
|
||||
(chargements / uploads GPU, etc.), il faudra isoler le runtime derrière un **module-pivot unique**
|
||||
(`lib/src/exec.rs`, une fonction `block_on`), seul fichier à modifier pour basculer de pollster
|
||||
vers tokio/futures-executor — le reste du code appelant `crate::exec::block_on(...)`.
|
||||
|
||||
Rappel : pollster et tokio sont des runtimes indépendants qui coexistent sans conflit dans un
|
||||
même binaire ; la seule contre-indication est de faire un `pollster::block_on` **à l'intérieur**
|
||||
d'un contexte async tokio (blocage imbriqué / deadlock possible).
|
||||
|
||||
+13
-2
@@ -12,14 +12,25 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
|
||||
> Basé sur l'architecture existante (ARCHI_APP, ARCHI_ARENES, ARCHI_CPU_GPU, ARCHI_RENDU).
|
||||
> Objectif : prototype fonctionnel d'abord, enrichissement progressif ensuite.
|
||||
>
|
||||
> **Point de départ (état réel au 2026-09-14 — la source de vérité est README.md).**
|
||||
> **Point de départ (état réel au 2026-09-16 — la source de vérité est README.md).**
|
||||
> Les fondations suivantes existent et fonctionnent déjà ; cette roadmap décrit la **trajectoire à
|
||||
> venir** à partir de cet état (elle reprend les étapes 1-4 du README avant la montée GPU-driven) :
|
||||
> - Workflow manuel (`Context` + `Renderer` + `PipelineCache`) : ✅ fonctionnel (exemple `manual`).
|
||||
> - Façade `App` / `AppBuilder` / `AppHandler` : 🚧 scaffold — boucle et présentation OK, mais `render()` ne peut pas encore dessiner (vue de frame non exposée) et le rendu de la scène n'est pas automatisé.
|
||||
> - Façade `App` / `AppBuilder` / `AppHandler` : ✅ **Scene auto-render** (2026-09-16) — la vue de frame
|
||||
> est exposée (`Frame::view()`), `render()` dessine la scène en une passe groupée
|
||||
> (`App::render_scene`) et la présentation est automatique dans `App::run` (exemple `simple`).
|
||||
> - `Scene` avec identifiants **String** (décision prise — voir tableau Notes de Décision) : 🚧 enregistrement seul.
|
||||
> - `Camera` / `Transform` et `glam` : types et mathématiques présents (`math/`, `resources/camera.rs`), non branchés au pipeline.
|
||||
|
||||
> **Étape suivante (prochaine itération) — « 3D + éclairage Phong » (ROADMAP 1.3 + 1.5).**
|
||||
> Le rendu automatique est aujourd'hui **plat** : le `basic_shader.wgsl` interprète les positions comme
|
||||
> déjà en NDC, sans matrice monde/vue/projection ni lumière. L'étape suivante rend la scène réellement
|
||||
> 3D et éclairée : créer `standard_shader.wgsl` (Phong : matrice `projection * view * world` + lumière
|
||||
> directionnelle), ajouter les uniform buffers (frame : view/proj/light ; par mesh : world matrix dérivée
|
||||
> du `Transform`) et les brancher dans `Renderer::render_scene` et `Material`, puis exposer `Camera`/
|
||||
> `Transform` à la `Scene` (caméra active) et ajouter un mesh de test (cube) à l'exemple. Objectif MVP :
|
||||
> **un mesh 3D éclairé à l'écran**.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1️⃣ — Prototype MVP : Un Mesh 3D éclairé à l'écran
|
||||
|
||||
@@ -72,6 +72,13 @@ pub trait AppHandler {
|
||||
- **`update()`** : appelé en premier. L'utilisateur peut modifier librement la scène (transformations, ajout/suppression d'entités). Ces modifications sont synchronisées vers le GPU via un **single buffer** Transform avant la passe de calcul.
|
||||
- **`render()`** : appelé après. Il ne sert qu'à injecter du rendu personnalisé (debug, HUD, etc.). La Scene reste immuable : aucune mutation d'état métier.
|
||||
|
||||
> **Note pour mémoire (init GPU / runtime async)** : depuis la migration winit 0.30, l'init GPU
|
||||
> se fait dans le callback synchrone `resumed()`, donc via `pollster::block_on(Context::new(...))`
|
||||
> dans `app.rs`. C'est actuellement le **seul** point de couplage de la lib à un runtime async.
|
||||
> On ne crée volontairement pas d'abstraction tant qu'il n'y a qu'un appel ; si la lib acquiert
|
||||
> d'autres appels async, isoler le runtime derrière un module-pivot unique (`exec::block_on`),
|
||||
> seul fichier à modifier pour basculer vers tokio/futures-executor. Voir PLAN.md (note pour mémoire).
|
||||
|
||||
## 4. Workflow et Cycle de Vie
|
||||
|
||||
### A. Initialisation (Configuration)
|
||||
|
||||
+2
-4
@@ -8,10 +8,8 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
wgpu = "30.0.0" # Vérifiez la version la plus récente
|
||||
winit = "0.29" # For window management — pinned to match examples
|
||||
winit = "0.30.13" # For window management — pinned to match examples
|
||||
thiserror = "2"
|
||||
bytemuck = { version = "1.25.0", features = ["derive"] }
|
||||
glam = "0.33"
|
||||
|
||||
[dev-dependencies]
|
||||
pollster = { version="0.4.0", features = ["macro"] }
|
||||
pollster = { version="1.0.1", features = ["macro"] }
|
||||
|
||||
+143
-84
@@ -1,6 +1,13 @@
|
||||
//! Workflow bas-niveau : utilisation directe du `Context`, `Renderer`, `PipelineCache`, `Mesh` et
|
||||
//! `Material`, contournant la façade `App`. Rendu d'un quad plat éclairé via la boucle winit 0.30
|
||||
//! (`EventLoop::run_app` + `ApplicationHandler`). La fenêtre et le GPU sont créés dans `resumed()`,
|
||||
//! comme l'exigent winit 0.30 et la migration faite dans `app.rs`.
|
||||
use std::sync::Arc;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
use wsg_lib::core::Context;
|
||||
use wsg_lib::core::Frame;
|
||||
use wsg_lib::core::Renderer;
|
||||
@@ -10,92 +17,144 @@ use wsg_lib::resources::Mesh;
|
||||
use wsg_lib::resources::Vertex;
|
||||
use wsg_lib::utils;
|
||||
|
||||
/// Application bas-niveau : détient les objets GPU + window, tous créés dans `resumed`.
|
||||
struct App {
|
||||
/// Fenêtre système, partagée via Arc (comme dans app.rs).
|
||||
window: Option<Arc<Window>>,
|
||||
/// Contexte GPU (Instance, Surface, Adapter, Device, Queue).
|
||||
context: Option<Context>,
|
||||
/// Couche d'exécution qui soumet les draw calls.
|
||||
renderer: Option<Renderer>,
|
||||
/// Cache de shaders/pipelines.
|
||||
cache: Option<PipelineCache>,
|
||||
/// Matériau (pipeline) du quad.
|
||||
material: Option<Material>,
|
||||
/// Mesh du quad (sommets + indices).
|
||||
mesh: Option<Mesh>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
/// Crée la fenêtre puis le GPU, et construit le mesh/matériau. Exécuté une fois au démarrage.
|
||||
/// Redondant `resumed` pour créer à nouveau ? double protection par `self.context.is_some()`.
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.context.is_some() {
|
||||
return;
|
||||
}
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
let attrs = WindowAttributes::default()
|
||||
.with_title("WSG Manual")
|
||||
.with_inner_size(LogicalSize::new(800.0, 600.0));
|
||||
let window = Arc::new(event_loop.create_window(attrs).unwrap());
|
||||
|
||||
// 1. Initialisation
|
||||
let context = pollster::block_on(Context::new(window.clone())).expect("Échec init GPU");
|
||||
|
||||
// Configuration de la surface et récupération du format
|
||||
let format = context
|
||||
.configure(&context.adapter, 800, 600)
|
||||
.expect("Échec configuration");
|
||||
|
||||
// 2. Initialisation du Renderer (Il récupère tout ce dont il a besoin)
|
||||
let device = Arc::new(context.device.clone());
|
||||
let mut cache = PipelineCache::new(device);
|
||||
cache
|
||||
.register_shader("basic", utils::BASIC_SHADER_PATH)
|
||||
.unwrap();
|
||||
|
||||
let renderer = Renderer::new(&context, format);
|
||||
|
||||
// 3. Material : On utilise renderer.device() et renderer.format()
|
||||
let material = Material::new(renderer.format(), "basic", &mut cache);
|
||||
|
||||
// Mesh : On utilise le device du renderer
|
||||
let vertices = [
|
||||
// Position (x,y,z) | Normale (x,y,z) | UV (u,v) | Couleur (r,g,b,a)
|
||||
Vertex {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 0.0],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
}, // Haut-Gauche (Rouge)
|
||||
Vertex {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 0.0],
|
||||
color: [0.0, 1.0, 0.0, 1.0],
|
||||
}, // Haut-Droite (Vert)
|
||||
Vertex {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 1.0],
|
||||
color: [0.0, 0.0, 1.0, 1.0],
|
||||
}, // Bas-Droite (Bleu)
|
||||
Vertex {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 1.0],
|
||||
color: [1.0, 1.0, 0.0, 1.0],
|
||||
}, // Bas-Gauche (Jaune)
|
||||
];
|
||||
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||
let mesh = Mesh::new(renderer.device(), &vertices, Some(&indices));
|
||||
|
||||
self.window = Some(window);
|
||||
self.context = Some(context);
|
||||
self.renderer = Some(renderer);
|
||||
self.cache = Some(cache);
|
||||
self.material = Some(material);
|
||||
self.mesh = Some(mesh);
|
||||
}
|
||||
|
||||
/// À chaque frame, demande un redessin pour un rendu continu (animation).
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
if let Some(window) = &self.window {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch des événements de fenêtre : RedrawRequested rend puis présente, CloseRequested quitte.
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
winit::event::WindowEvent::RedrawRequested => {
|
||||
if let (Some(context), Some(renderer), Some(mesh), Some(material)) =
|
||||
(&self.context, &self.renderer, &self.mesh, &self.material)
|
||||
{
|
||||
if let Some(frame) = Frame::try_new(&context.surface) {
|
||||
// 1. Rendu (plus d'arguments device/queue inutiles)
|
||||
renderer.render(frame.view(), mesh, material);
|
||||
|
||||
// 2. Présentation
|
||||
renderer.present(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
event_loop.exit(); // C'est ici que tu demandes à la boucle de s'arrêter
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!(
|
||||
"Répertoire courant : {:?}",
|
||||
std::env::current_dir().unwrap()
|
||||
);
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
||||
|
||||
// 1. Initialisation
|
||||
let context = pollster::block_on(Context::new(window.clone())).expect("Échec init GPU");
|
||||
|
||||
// Configuration de la surface et récupération du format
|
||||
let format = context
|
||||
.configure(&context.adapter, 800, 600)
|
||||
.expect("Échec configuration");
|
||||
|
||||
// 2. Initialisation du Renderer (Il récupère tout ce dont il a besoin)
|
||||
let device = Arc::new(context.device.clone());
|
||||
let mut cache = PipelineCache::new(device);
|
||||
cache
|
||||
.register_shader("basic", utils::BASIC_SHADER_PATH)
|
||||
.unwrap();
|
||||
|
||||
let renderer = Renderer::new(&context, format);
|
||||
|
||||
// 3. Material : On utilise renderer.device() et renderer.format()
|
||||
let material = Material::new(renderer.format(), "basic", &mut cache);
|
||||
|
||||
// Mesh : On utilise le device du renderer
|
||||
let vertices = [
|
||||
// Position (x,y,z) | Normale (x,y,z) | UV (u,v) | Couleur (r,g,b,a)
|
||||
Vertex {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 0.0],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
}, // Haut-Gauche (Rouge)
|
||||
Vertex {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 0.0],
|
||||
color: [0.0, 1.0, 0.0, 1.0],
|
||||
}, // Haut-Droite (Vert)
|
||||
Vertex {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 1.0],
|
||||
color: [0.0, 0.0, 1.0, 1.0],
|
||||
}, // Bas-Droite (Bleu)
|
||||
Vertex {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 1.0],
|
||||
color: [1.0, 1.0, 0.0, 1.0],
|
||||
}, // Bas-Gauche (Jaune)
|
||||
];
|
||||
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||
let mesh = Mesh::new(renderer.device(), &vertices, Some(&indices));
|
||||
|
||||
// Render loop
|
||||
event_loop
|
||||
.run(|event, elwt| {
|
||||
match event {
|
||||
winit::event::Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
if let Some(frame) = Frame::try_new(&context.surface) {
|
||||
// 1. Rendu (plus d'arguments device/queue inutiles)
|
||||
renderer.render(frame.view(), &mesh, &material);
|
||||
|
||||
// 2. Présentation
|
||||
renderer.present(frame);
|
||||
}
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
elwt.exit(); // C'est ici que tu demandes à la boucle de s'arrêter
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let mut app = App {
|
||||
window: None,
|
||||
context: None,
|
||||
renderer: None,
|
||||
cache: None,
|
||||
material: None,
|
||||
mesh: None,
|
||||
};
|
||||
event_loop.run_app(&mut app).unwrap();
|
||||
}
|
||||
|
||||
+56
-52
@@ -1,8 +1,9 @@
|
||||
//! Workflow déclaratif minimal, sans manipulation WGPU explicite dans ce fichier.
|
||||
//! `AppBuilder` ouvre la fenêtre, construit le `Context`/`Renderer` et fait tourner la boucle
|
||||
//! update → render → present. La scène se rend automatiquement : la méthode `render()` par défaut
|
||||
//! du trait `AppHandler` appelle `app.render_scene(frame.view())`, donc l'utilisateur n'implémente
|
||||
//! même pas `render` ici — il ne fait que remplir `app.scene` avec un mesh, un matériau et une entité.
|
||||
//! `AppBuilder` crée l'event loop puis `App::run` ouvre la fenêtre, construit le `Context`/`Renderer`
|
||||
//! et fait tourner la boucle update → render → present. Depuis la migration winit 0.30, le GPU n'existe
|
||||
//! qu'après `resumed` : c'est pourquoi l'enregistrement shader + la création mesh/matériau/entité vivent
|
||||
//! dans le hook `AppHandler::setup`, appelé une fois le contexte prêt. La scène se rend automatiquement :
|
||||
//! la méthode `render()` par défaut appelle `app.render_scene(frame.view())`.
|
||||
use std::sync::Arc;
|
||||
use wsg_lib::AppHandler;
|
||||
use wsg_lib::app::AppBuilder;
|
||||
@@ -11,56 +12,59 @@ use wsg_lib::utils::WsgError;
|
||||
|
||||
struct MonQuad;
|
||||
|
||||
impl AppHandler for MonQuad {}
|
||||
impl AppHandler for MonQuad {
|
||||
fn setup(&mut self, app: &mut wsg_lib::App) {
|
||||
let format = app.renderer().format();
|
||||
|
||||
// Enregistrement du shader, création du matériau et du mesh du quad (sans importer wgpu).
|
||||
app.cache()
|
||||
.register_shader("basic", wsg_lib::utils::BASIC_SHADER_PATH)
|
||||
.unwrap();
|
||||
let vertices = [
|
||||
Vertex {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 0.0],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
}, // Haut-Gauche (Rouge)
|
||||
Vertex {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 0.0],
|
||||
color: [0.0, 1.0, 0.0, 1.0],
|
||||
}, // Haut-Droite (Vert)
|
||||
Vertex {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 1.0],
|
||||
color: [0.0, 0.0, 1.0, 1.0],
|
||||
}, // Bas-Droite (Bleu)
|
||||
Vertex {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 1.0],
|
||||
color: [1.0, 1.0, 0.0, 1.0],
|
||||
}, // Bas-Gauche (Jaune)
|
||||
];
|
||||
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||
|
||||
let mesh = Arc::new(Mesh::new(
|
||||
app.renderer().device(),
|
||||
&vertices,
|
||||
Some(&indices),
|
||||
));
|
||||
let material = Arc::new(Material::new(format, "basic", app.cache()));
|
||||
|
||||
app.scene.add_mesh("quad_mesh", mesh).unwrap();
|
||||
app.scene.add_material("basic_material", material).unwrap();
|
||||
app.scene
|
||||
.add_entity("quad", "quad_mesh", "basic_material")
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[pollster::main]
|
||||
async fn main() -> Result<(), WsgError> {
|
||||
let mut app = AppBuilder::new().title("WSG Simple").build().await?;
|
||||
|
||||
// Enregistrement du shader, création du matériau et du mesh du quad (sans importer wgpu).
|
||||
app.cache
|
||||
.register_shader("basic", wsg_lib::utils::BASIC_SHADER_PATH)
|
||||
.unwrap();
|
||||
let vertices = [
|
||||
Vertex {
|
||||
position: [-0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 0.0],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
}, // Haut-Gauche (Rouge)
|
||||
Vertex {
|
||||
position: [0.5, 0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 0.0],
|
||||
color: [0.0, 1.0, 0.0, 1.0],
|
||||
}, // Haut-Droite (Vert)
|
||||
Vertex {
|
||||
position: [0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [1.0, 1.0],
|
||||
color: [0.0, 0.0, 1.0, 1.0],
|
||||
}, // Bas-Droite (Bleu)
|
||||
Vertex {
|
||||
position: [-0.5, -0.5, 0.0],
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
uv: [0.0, 1.0],
|
||||
color: [1.0, 1.0, 0.0, 1.0],
|
||||
}, // Bas-Gauche (Jaune)
|
||||
];
|
||||
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||
|
||||
let mesh = Arc::new(Mesh::new(app.renderer.device(), &vertices, Some(&indices)));
|
||||
let material = Arc::new(Material::new(
|
||||
app.renderer.format(),
|
||||
"basic",
|
||||
&mut app.cache,
|
||||
));
|
||||
|
||||
app.scene.add_mesh("quad_mesh", mesh).unwrap();
|
||||
app.scene.add_material("basic_material", material).unwrap();
|
||||
app.scene
|
||||
.add_entity("quad", "quad_mesh", "basic_material")
|
||||
.unwrap();
|
||||
|
||||
let app = AppBuilder::new().title("WSG Simple").build().await?;
|
||||
app.run(MonQuad)
|
||||
}
|
||||
|
||||
+196
-71
@@ -12,6 +12,14 @@
|
||||
//! - **scene::scene**: Exposes Scene as mutable field so users can register resources and entities.
|
||||
//! - **utils::conf**: Provides default window title, dimensions, and embedded WGSL source.
|
||||
//! - **handler**: Defines the AppHandler trait that users implement for custom logic.
|
||||
//!
|
||||
//! ## Architecture Note (winit 0.30)
|
||||
//! winit 0.30 removed the synchronous window-creation API (`WindowBuilder`) and the closure-based
|
||||
//! `EventLoop::run`, replacing them with the [`ApplicationHandler`] model driven by `EventLoop::run_app`.
|
||||
//! Windows can only be created inside `ApplicationHandler::resumed()`. Consequently this module builds
|
||||
//! the window and GPU context lazily inside `AppRunner`'s `resumed()` callback, and exposes the
|
||||
//! user-facing `AppBuilder::build → App::run` flow over that model. `AppHandler::setup()` is invoked
|
||||
//! once right after GPU initialization so users can register shaders/meshes/materials/entities.
|
||||
|
||||
use crate::AppHandler;
|
||||
use crate::core::{Context, Renderer};
|
||||
@@ -20,68 +28,96 @@ use crate::scene::Scene;
|
||||
use crate::utils::WsgError;
|
||||
use crate::utils::conf::{APP_DEFAULT_HEIGHT, APP_DEFAULT_TITLE, APP_DEFAULT_WIDTH};
|
||||
use std::sync::Arc;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
|
||||
/// High-level application facade that orchestrates window lifecycle, event loop, and rendering automation.
|
||||
/// Encapsulates all five WGPU objects (Instance, Surface, Adapter, Device, Queue) plus the render loop.
|
||||
/// Users create an App via AppBuilder, then run it with their implementation of AppHandler.
|
||||
///
|
||||
/// The GPU-facing fields (`context`, `renderer`, `window`, `cache`) are created lazily when the
|
||||
/// application is resumed (see `AppRunner`); they are only populated after `App::run` has started.
|
||||
/// Access them through the `context()`, `renderer()`, `window()` and `cache()` accessors, which is
|
||||
/// guaranteed to work inside `AppHandler::setup`, `update` and `render`.
|
||||
pub struct App {
|
||||
/// GPU hardware context — owns Instance, Surface, Adapter, Device, Queue lifecycle.
|
||||
pub context: Context,
|
||||
/// Executor layer — binds Materials and Meshes into RenderPasses during draw calls.
|
||||
pub renderer: Renderer,
|
||||
/// Winit event loop for window management. Set to None after run() consumes it.
|
||||
pub event_loop: Option<EventLoop<()>>, // On met en Option pour pouvoir faire .take() facilement
|
||||
/// Shader compilation cache — manages RenderPipelines keyed by shader_id.
|
||||
pub cache: PipelineCache,
|
||||
/// Resource depot and entity graph — users register Meshes/Materials here before the render loop begins.
|
||||
/// Resource depot and entity graph — users register Meshes/Materials here during `AppHandler::setup`.
|
||||
pub scene: Scene,
|
||||
/// Window title, read by the runner when the window is created in `resumed`.
|
||||
pub(crate) title: String,
|
||||
/// Window width, read by the runner when the window is created in `resumed`.
|
||||
pub(crate) width: u32,
|
||||
/// Window height, read by the runner when the window is created in `resumed`.
|
||||
pub(crate) height: u32,
|
||||
/// Winit event loop for window management. Set to None after run() consumes it.
|
||||
event_loop: Option<EventLoop<()>>, // On met en Option pour pouvoir faire .take() facilement
|
||||
/// GPU hardware context — owns Instance, Surface, Adapter, Device, Queue lifecycle.
|
||||
context: Option<Context>,
|
||||
/// Executor layer — binds Materials and Meshes into RenderPasses during draw calls.
|
||||
renderer: Option<Renderer>,
|
||||
/// The OS-level window backing this application. Shared via Arc for multi-owner access.
|
||||
pub window: Arc<Window>,
|
||||
window: Option<Arc<Window>>,
|
||||
/// Shader compilation cache — manages RenderPipelines keyed by shader_id.
|
||||
cache: Option<PipelineCache>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Returns a reference to the GPU renderer.
|
||||
/// Panics if called before `App::run` has created the renderer (i.e. before `resumed` fires).
|
||||
pub fn renderer(&self) -> &Renderer {
|
||||
self.renderer
|
||||
.as_ref()
|
||||
.expect("renderer not initialized yet — call app.run(handler) first")
|
||||
}
|
||||
|
||||
/// Returns a reference to the GPU hardware context.
|
||||
/// Panics if called before `App::run` has created the context (i.e. before `resumed` fires).
|
||||
pub fn context(&self) -> &Context {
|
||||
self.context
|
||||
.as_ref()
|
||||
.expect("context not initialized yet — call app.run(handler) first")
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the shader compilation cache.
|
||||
/// Panics if called before `App::run` has created the cache (i.e. before `resumed` fires).
|
||||
pub fn cache(&mut self) -> &mut PipelineCache {
|
||||
self.cache
|
||||
.as_mut()
|
||||
.expect("cache not initialized yet — call app.run(handler) first")
|
||||
}
|
||||
|
||||
/// Returns a reference to the window backing this application.
|
||||
/// Panics if called before `App::run` has created the window (i.e. before `resumed` fires).
|
||||
pub fn window(&self) -> &Window {
|
||||
self.window
|
||||
.as_ref()
|
||||
.expect("window not initialized yet — call app.run(handler) first")
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
/// Runs the application's main loop: processes events, updates logic per frame, renders, and presents.
|
||||
/// Inputs: handler — user-provided AppHandler implementation containing game logic.
|
||||
/// Returns Ok(()) on success or Err(WsgError::WindowSystem) if the event loop exits abnormally.
|
||||
/// Called once at application entry point; runs until the window is closed or an error occurs.
|
||||
/// Internal steps: 1) take EventLoop from Option → 2) enter winit event loop →
|
||||
/// 3a) on AboutToWait: call handler.update() + request_redraw →
|
||||
/// 3b) on RedrawRequested: acquire frame → call handler.render() → present frame →
|
||||
/// 3c) on CloseRequested: exit event loop.
|
||||
pub fn run<H: AppHandler + 'static>(mut self, mut handler: H) -> Result<(), WsgError> {
|
||||
/// Internal steps: 1) take EventLoop from Option → 2) build an `AppRunner` around the handler →
|
||||
/// 3) on resumed: create window/context/renderer/cache and call handler.setup() →
|
||||
/// 4) on about_to_wait: call handler.update() + request_redraw →
|
||||
/// 5) on RedrawRequested: acquire frame → call handler.render() → present frame →
|
||||
/// 6) on CloseRequested: exit the event loop.
|
||||
pub fn run<H: AppHandler + 'static>(mut self, handler: H) -> Result<(), WsgError> {
|
||||
// On extrait l'event_loop de manière sûre grâce au Option
|
||||
let event_loop = self.event_loop.take().ok_or(WsgError::WindowSystem)?; // Erreur si déjà pris
|
||||
let mut runner = AppRunner {
|
||||
title: self.title.clone(),
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
handler,
|
||||
app: None,
|
||||
};
|
||||
event_loop
|
||||
.run(move |event, elwt| {
|
||||
match event {
|
||||
winit::event::Event::AboutToWait => {
|
||||
// update logic
|
||||
handler.update(&mut self);
|
||||
self.window.request_redraw();
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
// Rendering logic
|
||||
let frame = self.context.get_next_frame();
|
||||
|
||||
// On appelle le render() de l'utilisateur (reçoit la frame courante)
|
||||
handler.render(&mut self, &frame);
|
||||
// On présente automatiquement
|
||||
self.renderer.present(frame);
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
elwt.exit();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.run_app(&mut runner)
|
||||
.map_err(|_| WsgError::WindowSystem)
|
||||
}
|
||||
|
||||
@@ -90,7 +126,7 @@ impl App {
|
||||
/// who override `render` to control drawing themselves.
|
||||
/// Inputs: view — the frame's texture view acting as the color attachment target.
|
||||
pub fn render_scene(&self, view: &wgpu::TextureView) {
|
||||
self.renderer.render_scene(view, &self.scene);
|
||||
self.renderer().render_scene(view, &self.scene);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,34 +164,123 @@ impl AppBuilder {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
/// Builds the configured `App` instance by creating all required components in order:
|
||||
/// EventLoop → Window → Context → Renderer → PipelineCache → Scene.
|
||||
/// Returns Ok(App) on success or Err(WsgError) if any component fails during creation.
|
||||
/// Called after setting desired properties via the builder pattern; triggers async GPU initialization.
|
||||
/// Builds the configured `App` instance: creates the event loop and stores the window
|
||||
/// configuration. The GPU context, window and renderer are created later, when the event loop
|
||||
/// is resumed (inside `App::run`), because winit 0.30 only allows window creation in that phase.
|
||||
/// Returns Ok(App) on success or Err(WsgError) if the event loop cannot be created.
|
||||
/// Called after setting desired properties via the builder pattern before `App::run`.
|
||||
pub async fn build(self) -> Result<App, WsgError> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = Arc::new(
|
||||
winit::window::WindowBuilder::new()
|
||||
.with_title(&self.title)
|
||||
.build(&event_loop)
|
||||
.map_err(|_| WsgError::WindowSystem)?,
|
||||
);
|
||||
let context = Context::new(window.clone()).await?;
|
||||
let device = Arc::new(context.device.clone());
|
||||
let format = context
|
||||
.configure(&context.adapter, self.width, self.height)
|
||||
.map_err(|_| WsgError::SurfaceIncompatible)?;
|
||||
let renderer = Renderer::new(&context, format);
|
||||
let cache = PipelineCache::new(device);
|
||||
let scene = Scene::new();
|
||||
|
||||
let event_loop = EventLoop::new().map_err(|_| WsgError::WindowSystem)?;
|
||||
Ok(App {
|
||||
context,
|
||||
renderer,
|
||||
cache,
|
||||
scene,
|
||||
scene: Scene::new(),
|
||||
title: self.title,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
event_loop: Some(event_loop),
|
||||
window,
|
||||
context: None,
|
||||
renderer: None,
|
||||
window: None,
|
||||
cache: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal runner that adapts a user `AppHandler` to winit's 0.30 `ApplicationHandler` model.
|
||||
/// It owns the window/GPU lifecycle: everything is created lazily inside `resumed()`, then the
|
||||
/// user's `setup`, `update` and `render` hooks are driven from the corresponding winit events.
|
||||
struct AppRunner<H: AppHandler> {
|
||||
/// Window title, applied when the window is created in `resumed`.
|
||||
title: String,
|
||||
/// Window width in pixels, applied when the window is created in `resumed`.
|
||||
width: u32,
|
||||
/// Window height in pixels, applied when the window is created in `resumed`.
|
||||
height: u32,
|
||||
/// The user-provided game logic.
|
||||
handler: H,
|
||||
/// The fully-built App facade, populated on the first `resumed` event.
|
||||
app: Option<App>,
|
||||
}
|
||||
|
||||
impl<H: AppHandler> ApplicationHandler for AppRunner<H> {
|
||||
/// Builds the window, GPU context, renderer and shader cache, then invokes the user's `setup`.
|
||||
/// Guarded so redundant back-to-back `resumed` events do not re-initialize the GPU.
|
||||
/// Inputs: event_loop — the active event loop used to create the window and control redrawing.
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.app.is_some() {
|
||||
return;
|
||||
}
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
let attrs = WindowAttributes::default()
|
||||
.with_title(&self.title)
|
||||
.with_inner_size(LogicalSize::new(self.width as f64, self.height as f64));
|
||||
let window = Arc::new(
|
||||
event_loop
|
||||
.create_window(attrs)
|
||||
.map_err(|_| WsgError::WindowSystem)
|
||||
.expect("failed to create window"),
|
||||
);
|
||||
|
||||
// Initialization GPU (bloquant, simplifié au max)
|
||||
let context = pollster::block_on(Context::new(window.clone())).expect("Échec init GPU");
|
||||
let format = context
|
||||
.configure(&context.adapter, self.width, self.height)
|
||||
.expect("Échec configuration surface");
|
||||
let device = Arc::new(context.device.clone());
|
||||
let cache = PipelineCache::new(device);
|
||||
let renderer = Renderer::new(&context, format);
|
||||
|
||||
let mut app = App {
|
||||
scene: Scene::new(),
|
||||
title: self.title.clone(),
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
event_loop: None,
|
||||
context: Some(context),
|
||||
renderer: Some(renderer),
|
||||
window: Some(window),
|
||||
cache: Some(cache),
|
||||
};
|
||||
// On laisse l'utilisateur enregistrer shaders/meshes/matériaux/entités une fois le GPU prêt.
|
||||
self.handler.setup(&mut app);
|
||||
self.app = Some(app);
|
||||
}
|
||||
|
||||
/// Drives the user's per-frame update and requests a redraw so the window renders continuously.
|
||||
/// Inputs: _event_loop — active event loop (unused here).
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
let Some(app) = self.app.as_mut() else {
|
||||
return;
|
||||
};
|
||||
self.handler.update(app);
|
||||
app.window().request_redraw();
|
||||
}
|
||||
|
||||
/// Dispatches window events: RedrawRequested renders/presents a frame, CloseRequested exits.
|
||||
/// Inputs: event_loop — active event loop, used to exit on close; event — the window event.
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let Some(app) = self.app.as_mut() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Rendering logic
|
||||
let frame = app.context().get_next_frame();
|
||||
|
||||
// On appelle le render() de l'utilisateur (reçoit la frame courante)
|
||||
self.handler.render(app, &frame);
|
||||
// On présente automatiquement
|
||||
app.renderer().present(frame);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
event_loop.exit();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ impl Renderer {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for (_label, mesh, material) in scene.iter_entities() {
|
||||
for (_label, mesh, material, _transform) in scene.iter_entities() {
|
||||
draw_entity(&mut render_pass, mesh, material);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ use crate::core::Frame;
|
||||
/// render (draw call execution). Default implementations provide empty update and automatic
|
||||
/// scene rendering for convenience.
|
||||
pub trait AppHandler {
|
||||
/// Called once by `App::run`, right after the window/GPU context are created (winit `resumed`).
|
||||
/// Use it to register shaders, build Meshes/Materials, and populate `app.scene` before the loop
|
||||
/// starts. This replaces the pre-`run` setup that was possible before the winit 0.30 migration.
|
||||
/// Default implementation does nothing.
|
||||
/// Inputs: app — mutable reference to the fully-initialized App facade.
|
||||
fn setup(&mut self, _app: &mut App) {}
|
||||
/// Called once per frame before rendering begins. Used for physics updates, input processing,
|
||||
/// entity management, and any other pre-render logic. Default implementation does nothing.
|
||||
/// Inputs: _app — mutable reference to the App facade providing access to all subsystems.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//! - `view_matrix()`: Computes the view matrix
|
||||
//! - `projection_matrix()`: Computes the projection matrix
|
||||
|
||||
use glam::{Vec3, Mat4};
|
||||
use glam::{Mat4, Vec3};
|
||||
|
||||
/// Represents a 3D camera for viewing the scene.
|
||||
///
|
||||
@@ -31,7 +31,11 @@ pub struct Camera {
|
||||
impl Camera {
|
||||
/// Creates a new camera with specified position, target, and up vector.
|
||||
pub fn new(position: Vec3, target: Vec3, up: Vec3) -> Self {
|
||||
Self { position, target, up }
|
||||
Self {
|
||||
position,
|
||||
target,
|
||||
up,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the view matrix for this camera.
|
||||
@@ -39,7 +43,7 @@ impl Camera {
|
||||
/// # Returns
|
||||
/// A `Mat4` representing the view transformation matrix
|
||||
pub fn view_matrix(&self) -> Mat4 {
|
||||
Mat4::look_at_rh(self.position, self.target, self.up)
|
||||
glam::camera::rh::view::look_at_mat4(self.position, self.target, self.up)
|
||||
}
|
||||
|
||||
/// Computes the projection matrix for this camera.
|
||||
@@ -53,6 +57,6 @@ impl Camera {
|
||||
/// # Returns
|
||||
/// A `Mat4` representing the projection transformation matrix
|
||||
pub fn projection_matrix(&self, fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
|
||||
Mat4::perspective_rh_gl(fov, aspect, near, far)
|
||||
glam::camera::rh::proj::opengl::perspective(fov, aspect, near, far)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
//! - `mesh::new()` uploads Vertex arrays from CPU memory into GPU vertex buffers via DeviceExt::create_buffer_init().
|
||||
//! - `material::new()` requests RenderPipelines from PipelineCache during scene initialization.
|
||||
|
||||
pub mod camera;
|
||||
pub mod material;
|
||||
pub mod mesh;
|
||||
pub mod vertex;
|
||||
|
||||
// Re-exports
|
||||
pub use camera::Camera;
|
||||
pub use material::Material;
|
||||
pub use mesh::Mesh;
|
||||
pub use vertex::Vertex;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//! # Entity Module
|
||||
//!
|
||||
//! Defines `Entity`, the renderable association between a Mesh and a Material together with its
|
||||
//! own world-space `Transform`. Each entry of `Scene::entities` is an `Entity`: it references the
|
||||
//! resource by identifier while carrying the per-entity placement data.
|
||||
//!
|
||||
//! ## Interaction with Other Modules
|
||||
//! - `scene::Scene` stores entities in a `HashMap<String, Entity>` keyed by label.
|
||||
//! - `math::Transform` provides the placement (translation / rotation / scale) converted to a
|
||||
//! matrix during rendering.
|
||||
//! - `resources::{Mesh, Material}` are the referenced render resources, resolved by `Scene`.
|
||||
|
||||
use crate::math::Transform;
|
||||
|
||||
/// A renderable entity: a mesh + material pair with its own world-space transform.
|
||||
///
|
||||
/// Entities are created through [`crate::scene::Scene::add_entity`] (identity transform) or
|
||||
/// [`crate::scene::Scene::add_entity_with_transform`]. Fields are exposed via accessors.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entity {
|
||||
/// Identifier of the referenced Mesh resource.
|
||||
mesh_id: String,
|
||||
/// Identifier of the referenced Material resource.
|
||||
material_id: String,
|
||||
/// World-space placement of this entity.
|
||||
transform: Transform,
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
/// Creates a new entity associating a mesh and a material under the given transform.
|
||||
/// Called internally by `Scene::add_entity*` after resource existence is validated.
|
||||
pub fn new(
|
||||
mesh_id: impl Into<String>,
|
||||
material_id: impl Into<String>,
|
||||
transform: Transform,
|
||||
) -> Self {
|
||||
Self {
|
||||
mesh_id: mesh_id.into(),
|
||||
material_id: material_id.into(),
|
||||
transform,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the identifier of the referenced Mesh resource.
|
||||
pub fn mesh_id(&self) -> &str {
|
||||
&self.mesh_id
|
||||
}
|
||||
|
||||
/// Returns the identifier of the referenced Material resource.
|
||||
pub fn material_id(&self) -> &str {
|
||||
&self.material_id
|
||||
}
|
||||
|
||||
/// Returns a reference to this entity's world-space transform.
|
||||
pub fn transform(&self) -> &Transform {
|
||||
&self.transform
|
||||
}
|
||||
|
||||
/// Sets this entity's world-space transform.
|
||||
/// Called by `Scene::set_entity_transform` during dynamic updates.
|
||||
pub fn set_transform(&mut self, transform: Transform) {
|
||||
self.transform = transform;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,9 @@
|
||||
//! all resources are declared before the render loop begins, while keeping the freedom to build the engine
|
||||
//! "brick by brick" through direct Context/PipelineCache/Renderer manipulation.
|
||||
|
||||
pub mod entity;
|
||||
pub mod scene;
|
||||
|
||||
// Re-export
|
||||
pub use entity::Entity;
|
||||
pub use scene::Scene;
|
||||
|
||||
+51
-12
@@ -10,20 +10,22 @@
|
||||
//! and avoiding borrow checker issues during dynamic updates.
|
||||
//! - **Ergonomie**: Users interact only with entity-level operations (add/remove/get) rather than wgpu buffers/pipelines directly.
|
||||
|
||||
use crate::math::Transform;
|
||||
use crate::resources::{Material, Mesh};
|
||||
use crate::scene::Entity;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Resource depot and entity graph. Stores Meshes and Materials keyed by identifier strings,
|
||||
/// and maps entity labels to their associated mesh+material pairs for rendering iteration.
|
||||
/// and maps entity labels to their associated `Entity` (mesh + material + transform) for rendering iteration.
|
||||
/// Created once during application setup; entities are added before the render loop starts.
|
||||
pub struct Scene {
|
||||
/// Map of mesh identifiers to owned `Arc<Mesh>` instances. Populated via `add_mesh()`.
|
||||
meshes: HashMap<String, Arc<Mesh>>,
|
||||
/// Map of material identifiers to owned `Arc<Material>` instances. Populated via `add_material()`.
|
||||
materials: HashMap<String, Arc<Material>>,
|
||||
/// Map of entity labels to (mesh_id, material_id) associations. Populated via `add_entity()`.
|
||||
entities: HashMap<String, (String, String)>,
|
||||
/// Map of entity labels to `Entity` associations. Populated via `add_entity()` / `add_entity_with_transform()`.
|
||||
entities: HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@@ -59,17 +61,33 @@ impl Scene {
|
||||
Ok(id.to_string())
|
||||
}
|
||||
|
||||
/// Associates an entity label with a mesh and material pair for rendering iteration.
|
||||
/// Associates an entity label with a mesh and material pair for rendering iteration, using an identity transform.
|
||||
/// Inputs: label (entity identifier string), mesh_id (key into meshes map), material_id (key into materials map).
|
||||
/// Returns Ok(label) on success or Err(String) if either referenced resource does not exist.
|
||||
/// Called during scene initialization to build the renderable entity graph.
|
||||
/// Internal steps: 1) validate mesh_id exists → 2) validate material_id exists →
|
||||
/// 3) insert association into entities HashMap.
|
||||
/// 3) insert an `Entity` with identity transform into the entities HashMap.
|
||||
pub fn add_entity(
|
||||
&mut self,
|
||||
label: &str,
|
||||
mesh_id: &str,
|
||||
material_id: &str,
|
||||
) -> Result<String, String> {
|
||||
self.add_entity_with_transform(label, mesh_id, material_id, Transform::identity())
|
||||
}
|
||||
|
||||
/// Associates an entity label with a mesh and material pair together with an explicit world-space transform.
|
||||
/// Inputs: label (entity identifier string), mesh_id (key into meshes map), material_id (key into materials map),
|
||||
/// transform (world-space placement). Returns Ok(label) on success or Err(String) if either referenced resource does not exist.
|
||||
/// Called during scene initialization to build the renderable entity graph.
|
||||
/// Internal steps: 1) validate mesh_id exists → 2) validate material_id exists →
|
||||
/// 3) insert the `Entity` into the entities HashMap.
|
||||
pub fn add_entity_with_transform(
|
||||
&mut self,
|
||||
label: &str,
|
||||
mesh_id: &str,
|
||||
material_id: &str,
|
||||
transform: Transform,
|
||||
) -> Result<String, String> {
|
||||
if !self.meshes.contains_key(mesh_id) {
|
||||
return Err(format!("Mesh '{}' does not exist.", mesh_id));
|
||||
@@ -79,7 +97,7 @@ impl Scene {
|
||||
}
|
||||
self.entities.insert(
|
||||
label.to_string(),
|
||||
(mesh_id.to_string(), material_id.to_string()),
|
||||
Entity::new(mesh_id, material_id, transform),
|
||||
);
|
||||
Ok(label.to_string())
|
||||
}
|
||||
@@ -96,16 +114,37 @@ impl Scene {
|
||||
self.materials.get(id)
|
||||
}
|
||||
|
||||
/// Iterates all entity associations, yielding (label, mesh_ref, material_ref) triples.
|
||||
/// Iterates all entity associations, yielding (label, mesh_ref, material_ref, transform_ref) tuples.
|
||||
/// Called by the orchestrator during each render pass to draw every entity in order.
|
||||
pub fn iter_entities(&self) -> impl Iterator<Item = (&str, &Arc<Mesh>, &Arc<Material>)> + '_ {
|
||||
self.entities.iter().map(|(label, (mesh_id, mat_id))| {
|
||||
let mesh = self.meshes.get(mesh_id).unwrap(); // safe: add_entity validates existence
|
||||
let mat = self.materials.get(mat_id).unwrap(); // same invariant
|
||||
(label.as_str(), mesh, mat)
|
||||
pub fn iter_entities(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&str, &Arc<Mesh>, &Arc<Material>, &Transform)> + '_ {
|
||||
self.entities.iter().map(|(label, entity)| {
|
||||
let mesh = self.meshes.get(entity.mesh_id()).unwrap(); // safe: add_entity validates existence
|
||||
let mat = self.materials.get(entity.material_id()).unwrap(); // same invariant
|
||||
(label.as_str(), mesh, mat, entity.transform())
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the transform of the entity with the given label, if it exists.
|
||||
/// Called by the user to read an entity's current placement during updates.
|
||||
pub fn entity_transform(&self, label: &str) -> Option<&Transform> {
|
||||
self.entities.get(label).map(|e| e.transform())
|
||||
}
|
||||
|
||||
/// Overwrites the transform of the entity with the given label.
|
||||
/// Returns true if the entity existed and was updated, false otherwise.
|
||||
/// Called by the user to move/rotate/scale an entity during `AppHandler::update`.
|
||||
pub fn set_entity_transform(&mut self, label: &str, transform: Transform) -> bool {
|
||||
match self.entities.get_mut(label) {
|
||||
Some(entity) => {
|
||||
entity.set_transform(transform);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an entity from the graph without freeing its underlying resources.
|
||||
/// The referenced Mesh and Material remain registered; only the association is dropped.
|
||||
/// Called during dynamic updates when an entity should be hidden or removed temporarily.
|
||||
|
||||
Reference in New Issue
Block a user