PoC: Glitchless rendering using bounce buffers and swapchain
This commit is contained in:
parent
376416c32e
commit
95464568c7
139
firmware/Cargo.lock
generated
139
firmware/Cargo.lock
generated
|
|
@ -30,7 +30,7 @@ dependencies = [
|
||||||
"esp-backtrace",
|
"esp-backtrace",
|
||||||
"esp-bootloader-esp-idf",
|
"esp-bootloader-esp-idf",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"esp-println",
|
"esp-println",
|
||||||
"esp-radio",
|
"esp-radio",
|
||||||
"esp-rtos",
|
"esp-rtos",
|
||||||
|
|
@ -47,6 +47,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"mutually_exclusive_features",
|
"mutually_exclusive_features",
|
||||||
|
"ouroboros",
|
||||||
"panic-rtt-target",
|
"panic-rtt-target",
|
||||||
"password-hash 0.1.0",
|
"password-hash 0.1.0",
|
||||||
"pastey 0.2.1",
|
"pastey 0.2.1",
|
||||||
|
|
@ -105,6 +106,12 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aliasable"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aligned"
|
name = "aligned"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
@ -2029,8 +2036,6 @@ checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-alloc"
|
name = "esp-alloc"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "641e43d6a60244429117ef2fa7a47182120c7561336ea01f6fb08d634f46bae1"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2 0.3.1",
|
"allocator-api2 0.3.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
@ -2045,13 +2050,11 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-backtrace"
|
name = "esp-backtrace"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"document-features",
|
"document-features",
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-println",
|
"esp-println",
|
||||||
"heapless 0.9.2",
|
"heapless 0.9.2",
|
||||||
"riscv",
|
"riscv",
|
||||||
|
|
@ -2062,15 +2065,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-bootloader-esp-idf"
|
name = "esp-bootloader-esp-idf"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02a56964ab5479ac20c9cf76fa3b0d3f2233b20b5d8554e81ef5d65f63c20567"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"document-features",
|
"document-features",
|
||||||
"embedded-storage",
|
"embedded-storage",
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-hal-procmacros",
|
"esp-hal-procmacros",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-rom-sys",
|
"esp-rom-sys",
|
||||||
"jiff",
|
"jiff",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -2080,11 +2081,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-config"
|
name = "esp-config"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"somni-expr",
|
"somni-expr",
|
||||||
|
|
@ -2093,8 +2092,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-hal"
|
name = "esp-hal"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitfield 0.19.4",
|
"bitfield 0.19.4",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
|
|
@ -2119,7 +2116,7 @@ dependencies = [
|
||||||
"enumset",
|
"enumset",
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-hal-procmacros",
|
"esp-hal-procmacros",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-riscv-rt",
|
"esp-riscv-rt",
|
||||||
"esp-rom-sys",
|
"esp-rom-sys",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
|
|
@ -2149,8 +2146,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-hal-procmacros"
|
name = "esp-hal-procmacros"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3e025a7a7a0affdb4ff913b5c4494aef96ee03d085bf83c27453ae3a71d50da6"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"object",
|
"object",
|
||||||
|
|
@ -2161,6 +2156,10 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "esp-metadata-generated"
|
||||||
|
version = "0.3.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-metadata-generated"
|
name = "esp-metadata-generated"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
@ -2170,26 +2169,22 @@ checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-phy"
|
name = "esp-phy"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"document-features",
|
"document-features",
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
"esp-wifi-sys",
|
"esp-wifi-sys-esp32s3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-println"
|
name = "esp-println"
|
||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
"log",
|
"log",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
|
|
@ -2198,8 +2193,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-radio"
|
name = "esp-radio"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "684c4de2f8907b73c9b891fbda65286a86d34fced4b856f36a7896c211f2f265"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2 0.3.1",
|
"allocator-api2 0.3.1",
|
||||||
"bt-hci",
|
"bt-hci",
|
||||||
|
|
@ -2213,11 +2206,11 @@ dependencies = [
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-hal-procmacros",
|
"esp-hal-procmacros",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-phy",
|
"esp-phy",
|
||||||
"esp-radio-rtos-driver",
|
"esp-radio-rtos-driver",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
"esp-wifi-sys",
|
"esp-wifi-sys-esp32s3",
|
||||||
"heapless 0.9.2",
|
"heapless 0.9.2",
|
||||||
"instability",
|
"instability",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
|
|
@ -2230,14 +2223,10 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-radio-rtos-driver"
|
name = "esp-radio-rtos-driver"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "543bc31d1851afd062357e7810c1a9633f282fd3993583499a841ab497cbca6c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-riscv-rt"
|
name = "esp-riscv-rt"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "502744a5b1e7268d27fd2a4e56ad45efe42ead517d6c517a6961540de949b0ee"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"riscv",
|
"riscv",
|
||||||
|
|
@ -2247,19 +2236,15 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-rom-sys"
|
name = "esp-rom-sys"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd66cccc6dd2d13e9f33668a57717ab14a6d217180ec112e6be533de93e7ecbf"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"document-features",
|
"document-features",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-rtos"
|
name = "esp-rtos"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "162ec711c8d06e79c67b75d01595539e86b0aac209643af98ca87a12250428b3"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2 0.3.1",
|
"allocator-api2 0.3.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
@ -2271,7 +2256,7 @@ dependencies = [
|
||||||
"esp-config",
|
"esp-config",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-hal-procmacros",
|
"esp-hal-procmacros",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-radio-rtos-driver",
|
"esp-radio-rtos-driver",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
|
|
@ -2280,14 +2265,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-storage"
|
name = "esp-storage"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1495fc1f5549bdd840b52d9ceb201746200e1620d2636f46958c11e765623b80"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"embedded-storage",
|
"embedded-storage",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"esp-hal-procmacros",
|
"esp-hal-procmacros",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"esp-rom-sys",
|
"esp-rom-sys",
|
||||||
"esp-sync",
|
"esp-sync",
|
||||||
]
|
]
|
||||||
|
|
@ -2295,14 +2278,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-sync"
|
name = "esp-sync"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d44974639b4e88914f83fe60d2832c00276657d7d857628fdfc966cc7302e8a8"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"document-features",
|
"document-features",
|
||||||
"embassy-sync 0.6.2",
|
"embassy-sync 0.6.2",
|
||||||
"embassy-sync 0.7.2",
|
"embassy-sync 0.7.2",
|
||||||
"esp-metadata-generated",
|
"esp-metadata-generated 0.3.0",
|
||||||
"log",
|
"log",
|
||||||
"riscv",
|
"riscv",
|
||||||
"xtensa-lx",
|
"xtensa-lx",
|
||||||
|
|
@ -2322,13 +2303,9 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp-wifi-sys"
|
name = "esp-wifi-sys-esp32s3"
|
||||||
version = "0.8.1"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/esp-rs/esp-wifi-sys?rev=7623c8d#7623c8d746b55cd8d9f7473359069aef381b7d3b"
|
||||||
checksum = "89b6544f6f0cb86169d1f93ba2101a8d50358a040c5043676ed86b793e09b12c"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "esp32"
|
name = "esp32"
|
||||||
|
|
@ -3780,6 +3757,12 @@ dependencies = [
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -5353,6 +5336,30 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ouroboros"
|
||||||
|
version = "0.18.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
|
||||||
|
dependencies = [
|
||||||
|
"aliasable",
|
||||||
|
"ouroboros_macro",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ouroboros_macro"
|
||||||
|
version = "0.18.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.4.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"proc-macro2-diagnostics",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.114",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p256"
|
name = "p256"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
|
|
@ -5761,6 +5768,19 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2-diagnostics"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.114",
|
||||||
|
"version_check",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prodash"
|
name = "prodash"
|
||||||
version = "31.0.0"
|
version = "31.0.0"
|
||||||
|
|
@ -6706,7 +6726,7 @@ dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
"flate2",
|
"flate2",
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"regex",
|
"regex",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
@ -6855,7 +6875,6 @@ checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spectre-api-sys"
|
name = "spectre-api-sys"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/Limeth/spectre-api-sys?rev=9e844eb056c3dfee8286ac21ec40fa689a8b8aa2#9e844eb056c3dfee8286ac21ec40fa689a8b8aa2"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
|
|
@ -6941,7 +6960,7 @@ version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.114",
|
"syn 2.0.114",
|
||||||
|
|
@ -8449,8 +8468,6 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtensa-lx"
|
name = "xtensa-lx"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e012d667b0aa6d2592ace8ef145a98bff3e76cca7a644f4181ecd7a916ed289b"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"critical-section",
|
"critical-section",
|
||||||
]
|
]
|
||||||
|
|
@ -8458,8 +8475,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtensa-lx-rt"
|
name = "xtensa-lx-rt"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"xtensa-lx",
|
"xtensa-lx",
|
||||||
|
|
@ -8469,8 +8484,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtensa-lx-rt-proc-macros"
|
name = "xtensa-lx-rt-proc-macros"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -8503,6 +8516,12 @@ dependencies = [
|
||||||
"hashlink",
|
"hashlink",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yazi"
|
name = "yazi"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] }
|
||||||
esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
|
esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||||
indoc = "2.0.7"
|
indoc = "2.0.7"
|
||||||
|
ouroboros = "0.18.5"
|
||||||
|
|
||||||
# A fork of slint with patches for `allocator_api` support.
|
# A fork of slint with patches for `allocator_api` support.
|
||||||
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use log::LevelFilter;
|
||||||
|
|
||||||
pub const LOG_LEVEL_FILTER: LevelFilter = {
|
pub const LOG_LEVEL_FILTER: LevelFilter = {
|
||||||
if let Some(string) = option_env!("ESP_LOG") {
|
if let Some(string) = option_env!("ESP_LOG") {
|
||||||
if string.eq_ignore_ascii_case("ERROR") {
|
if string.eq_ignore_ascii_case("OFF") {
|
||||||
|
LevelFilter::Off
|
||||||
|
} else if string.eq_ignore_ascii_case("ERROR") {
|
||||||
LevelFilter::Error
|
LevelFilter::Error
|
||||||
} else if string.eq_ignore_ascii_case("WARN") {
|
} else if string.eq_ignore_ascii_case("WARN") {
|
||||||
LevelFilter::Warn
|
LevelFilter::Warn
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ use alloc::vec::Vec;
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||||
use embassy_embedded_hal::flash::partition::Partition;
|
use embassy_embedded_hal::flash::partition::Partition;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::{SendSpawner, Spawner, SpawnerTraceExt};
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::channel::Channel;
|
use embassy_sync::channel::Channel;
|
||||||
use embassy_sync::mutex::Mutex;
|
use embassy_sync::mutex::Mutex;
|
||||||
|
|
@ -88,8 +88,8 @@ use {esp_alloc as _, esp_backtrace as _};
|
||||||
use crate::matrix::IoeMatrix;
|
use crate::matrix::IoeMatrix;
|
||||||
use crate::peripherals::st7701s::St7701s;
|
use crate::peripherals::st7701s::St7701s;
|
||||||
use crate::proxy::create_hid_report_interceptor;
|
use crate::proxy::create_hid_report_interceptor;
|
||||||
use crate::ui::backend::{FramebufferPtr, SlintBackend};
|
use crate::ui::backend::SlintBackend;
|
||||||
use crate::ui::dpi::{DmaBounce, DmaTxBounceBuf, Framebuffer, allocate_dma_buffer_in};
|
use crate::ui::dpi::{DmaBounce, DmaTxBounceBuf, Framebuffer, Swapchain, allocate_dma_buffer_in};
|
||||||
use crate::vial::{
|
use crate::vial::{
|
||||||
CustomKeycodes, VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID, VIAL_KEYBOARD_NAME, VIAL_PRODUCT_ID,
|
CustomKeycodes, VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID, VIAL_KEYBOARD_NAME, VIAL_PRODUCT_ID,
|
||||||
VIAL_VENDOR_ID,
|
VIAL_VENDOR_ID,
|
||||||
|
|
@ -178,14 +178,25 @@ async fn test_bounce_buffers(
|
||||||
internal_memory: InternalBurstConfig::Enabled,
|
internal_memory: InternalBurstConfig::Enabled,
|
||||||
external_memory: EXTERNAL_BURST_CONFIG,
|
external_memory: EXTERNAL_BURST_CONFIG,
|
||||||
};
|
};
|
||||||
let buffer_src = Box::leak(allocate_dma_buffer_in(
|
let (swapchain_reader, mut swapchain_writer) = Swapchain {
|
||||||
|
framebuffers: [
|
||||||
|
Box::leak(allocate_dma_buffer_in(
|
||||||
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||||
burst_config,
|
burst_config,
|
||||||
&PSRAM_ALLOCATOR,
|
&PSRAM_ALLOCATOR,
|
||||||
));
|
)),
|
||||||
|
Box::leak(allocate_dma_buffer_in(
|
||||||
|
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||||
|
burst_config,
|
||||||
|
&PSRAM_ALLOCATOR,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
.into_reader_writer();
|
||||||
|
|
||||||
{
|
{
|
||||||
let buffer_src = bytemuck::cast_slice_mut::<u8, Rgb565Pixel>(buffer_src);
|
let write_guard = &mut swapchain_writer.write();
|
||||||
|
let buffer_src = write_guard.cast::<Rgb565Pixel>();
|
||||||
let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2)
|
let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2)
|
||||||
.rev()
|
.rev()
|
||||||
.map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0))
|
.map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0))
|
||||||
|
|
@ -212,19 +223,19 @@ async fn test_bounce_buffers(
|
||||||
warn!("WIDTH_STORED_PIXELS: {WIDTH_STORED_PIXELS}");
|
warn!("WIDTH_STORED_PIXELS: {WIDTH_STORED_PIXELS}");
|
||||||
warn!("ROWS_PER_WINDOW: {ROWS_PER_WINDOW}");
|
warn!("ROWS_PER_WINDOW: {ROWS_PER_WINDOW}");
|
||||||
|
|
||||||
let mut buf = DmaBounce::new(
|
let buf = DmaBounce::new(
|
||||||
Global,
|
Global,
|
||||||
channel,
|
channel,
|
||||||
AnySpi::from(peripheral),
|
AnySpi::from(peripheral),
|
||||||
st7701s.dpi,
|
st7701s.dpi,
|
||||||
buffer_src,
|
swapchain_reader,
|
||||||
FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL,
|
FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL,
|
||||||
WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||||
ROWS_PER_WINDOW,
|
ROWS_PER_WINDOW,
|
||||||
burst_config,
|
burst_config,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
buf.send().await;
|
buf.launch_interrupt_driven_task().await;
|
||||||
error!("TEST BOUNCE BUFFERS SECTION DONE");
|
error!("TEST BOUNCE BUFFERS SECTION DONE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,11 +332,17 @@ async fn main(_spawner: Spawner) {
|
||||||
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||||
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
||||||
let executor_core_0 = EXECUTOR_CORE_0.init(executor_core_0);
|
let executor_core_0 = EXECUTOR_CORE_0.init(executor_core_0);
|
||||||
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority3);
|
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority1);
|
||||||
|
|
||||||
|
// static EXECUTOR_CORE_1: StaticCell<InterruptExecutor<3>> = StaticCell::new();
|
||||||
|
// let executor_core_1 = InterruptExecutor::new(software_interrupt.software_interrupt3);
|
||||||
|
// let executor_core_1 = EXECUTOR_CORE_1.init(executor_core_1);
|
||||||
|
// let interrupt_core_1_spawner = executor_core_1.start(interrupt::Priority::Priority2);
|
||||||
|
|
||||||
info!("ESP-RTOS started!");
|
info!("ESP-RTOS started!");
|
||||||
|
|
||||||
let main_task_peripherals = MainPeripherals {
|
let main_task_peripherals = MainPeripherals {
|
||||||
|
// high_priority_task_spawner: interrupt_core_1_spawner,
|
||||||
uart_rx,
|
uart_rx,
|
||||||
software_interrupt1: software_interrupt.software_interrupt1,
|
software_interrupt1: software_interrupt.software_interrupt1,
|
||||||
RNG: peripherals.RNG,
|
RNG: peripherals.RNG,
|
||||||
|
|
@ -376,6 +393,7 @@ async fn main(_spawner: Spawner) {
|
||||||
/// Peripherals passed to the main task.
|
/// Peripherals passed to the main task.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
struct MainPeripherals {
|
struct MainPeripherals {
|
||||||
|
// high_priority_task_spawner: SendSpawner,
|
||||||
uart_rx: UartRx<'static, Blocking>,
|
uart_rx: UartRx<'static, Blocking>,
|
||||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||||
RNG: esp_hal::peripherals::RNG<'static>,
|
RNG: esp_hal::peripherals::RNG<'static>,
|
||||||
|
|
@ -706,14 +724,14 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
112,
|
112,
|
||||||
368 - 112,
|
368 - 112,
|
||||||
960,
|
960,
|
||||||
16,
|
8,
|
||||||
false,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
info!("Framebuffer created!");
|
info!("Framebuffer created!");
|
||||||
|
|
||||||
let window_size = [framebuffer.height, framebuffer.width];
|
let window_size = [framebuffer.height, framebuffer.width];
|
||||||
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
let swapchain_writer = framebuffer.swapchain.take().unwrap();
|
||||||
|
|
||||||
static SECOND_CORE_STACK: StaticCell<Stack<STACK_SIZE_CORE_APP>> = StaticCell::new();
|
static SECOND_CORE_STACK: StaticCell<Stack<STACK_SIZE_CORE_APP>> = StaticCell::new();
|
||||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||||
|
|
@ -736,7 +754,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
// peripherals: RefCell::new(Some(peripherals)),
|
// peripherals: RefCell::new(Some(peripherals)),
|
||||||
window_size,
|
window_size,
|
||||||
window: RefCell::new(None),
|
window: RefCell::new(None),
|
||||||
framebuffer: framebuffer_ptr,
|
swapchain: RefCell::new(swapchain_writer),
|
||||||
quit_event_loop: Default::default(),
|
quit_event_loop: Default::default(),
|
||||||
events: Arc::new(critical_section::Mutex::new(RefCell::new(VecDeque::new()))),
|
events: Arc::new(critical_section::Mutex::new(RefCell::new(VecDeque::new()))),
|
||||||
};
|
};
|
||||||
|
|
@ -751,6 +769,19 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
|
|
||||||
info!("Awaiting on all tasks...");
|
info!("Awaiting on all tasks...");
|
||||||
|
|
||||||
|
// let spawner = unsafe { Spawner::for_current_executor() }.await;
|
||||||
|
|
||||||
|
// peripherals
|
||||||
|
// .high_priority_task_spawner
|
||||||
|
// .must_spawn(run_bounce_buffers(framebuffer));
|
||||||
|
|
||||||
|
framebuffer
|
||||||
|
.bounce_buffers
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.launch_interrupt_driven_task()
|
||||||
|
.await;
|
||||||
|
|
||||||
// TODO: Probably want to select! instead and re-try.
|
// TODO: Probably want to select! instead and re-try.
|
||||||
join_all![
|
join_all![
|
||||||
run_alloc_stats_reporter(),
|
run_alloc_stats_reporter(),
|
||||||
|
|
@ -762,7 +793,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
// warn!("Waited.");
|
// warn!("Waited.");
|
||||||
// framebuffer.bounce_buffers.send().await;
|
// framebuffer.bounce_buffers.send().await;
|
||||||
// },
|
// },
|
||||||
framebuffer.bounce_buffers.send(),
|
// framebuffer.bounce_buffers.send(),
|
||||||
// ui::dpi::run_lcd(st7701s, framebuffer),
|
// ui::dpi::run_lcd(st7701s, framebuffer),
|
||||||
// lcd_task,
|
// lcd_task,
|
||||||
run_devices! (
|
run_devices! (
|
||||||
|
|
@ -785,6 +816,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn run_bounce_buffers(framebuffer: &'static mut Framebuffer) {
|
||||||
|
framebuffer.bounce_buffers.as_mut().unwrap().send().await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn run_alloc_stats_reporter() {
|
async fn run_alloc_stats_reporter() {
|
||||||
let mut psram_used_prev = 0;
|
let mut psram_used_prev = 0;
|
||||||
let mut heap_used_prev = 0;
|
let mut heap_used_prev = 0;
|
||||||
|
|
|
||||||
|
|
@ -1276,7 +1276,7 @@ where
|
||||||
//
|
//
|
||||||
// Adafruit would use 11 MHz.
|
// Adafruit would use 11 MHz.
|
||||||
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
||||||
.with_frequency(Rate::from_mhz(7)) // From Adafruit
|
.with_frequency(Rate::from_mhz(5)) // From Adafruit
|
||||||
.with_clock_mode(ClockMode {
|
.with_clock_mode(ClockMode {
|
||||||
polarity: Polarity::IdleLow, // From Adafruit
|
polarity: Polarity::IdleLow, // From Adafruit
|
||||||
phase: Phase::ShiftHigh, // From Adafruit
|
phase: Phase::ShiftHigh, // From Adafruit
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,17 @@ use slint::{
|
||||||
};
|
};
|
||||||
use xkbcommon::xkb::{self, Keysym};
|
use xkbcommon::xkb::{self, Keysym};
|
||||||
|
|
||||||
use crate::proxy::{KEY_MESSAGE_CHANNEL, TryFromKeysym};
|
use crate::{
|
||||||
|
proxy::{KEY_MESSAGE_CHANNEL, TryFromKeysym},
|
||||||
|
ui::dpi::SwapchainWriter,
|
||||||
|
};
|
||||||
|
|
||||||
use super::window_adapter::SoftwareWindowAdapter;
|
use super::window_adapter::SoftwareWindowAdapter;
|
||||||
|
|
||||||
pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);
|
|
||||||
|
|
||||||
unsafe impl Send for FramebufferPtr {}
|
|
||||||
|
|
||||||
pub struct SlintBackend {
|
pub struct SlintBackend {
|
||||||
pub window_size: [u32; 2],
|
pub window_size: [u32; 2],
|
||||||
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
|
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
|
||||||
pub framebuffer: FramebufferPtr,
|
pub swapchain: RefCell<SwapchainWriter>,
|
||||||
pub quit_event_loop: Arc<AtomicBool>,
|
pub quit_event_loop: Arc<AtomicBool>,
|
||||||
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
|
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
|
||||||
}
|
}
|
||||||
|
|
@ -39,9 +38,8 @@ impl slint::platform::Platform for SlintBackend {
|
||||||
fn create_window_adapter(
|
fn create_window_adapter(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
||||||
let renderer = SoftwareRenderer::new_with_repaint_buffer_type(
|
let renderer =
|
||||||
RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */
|
SoftwareRenderer::new_with_repaint_buffer_type(RepaintBufferType::SwappedBuffers);
|
||||||
);
|
|
||||||
renderer.set_rendering_rotation(RenderingRotation::Rotate270);
|
renderer.set_rendering_rotation(RenderingRotation::Rotate270);
|
||||||
let window = SoftwareWindowAdapter::new(renderer);
|
let window = SoftwareWindowAdapter::new(renderer);
|
||||||
// window.set_scale_factor(4.0);
|
// window.set_scale_factor(4.0);
|
||||||
|
|
@ -134,8 +132,10 @@ impl slint::platform::Platform for SlintBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.draw_if_needed(|renderer| {
|
window.draw_if_needed(|renderer| {
|
||||||
// TODO: Proper synchronization.
|
// TODO: VSYNC support.
|
||||||
let framebuffer = unsafe { &mut *self.framebuffer.0 };
|
let mut swapchain = self.swapchain.borrow_mut();
|
||||||
|
let mut write_guard = swapchain.write();
|
||||||
|
let framebuffer = write_guard.cast::<Rgb565Pixel>();
|
||||||
renderer.render(framebuffer, self.window_size[1] as usize);
|
renderer.render(framebuffer, self.window_size[1] as usize);
|
||||||
info!("UI rendered.");
|
info!("UI rendered.");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use core::{
|
use core::{
|
||||||
alloc::Layout,
|
alloc::Layout,
|
||||||
|
cell::UnsafeCell,
|
||||||
|
marker::PhantomData,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::atomic::{self, AtomicBool, AtomicU32, AtomicUsize},
|
sync::atomic::{self, AtomicBool, AtomicU32, AtomicUsize},
|
||||||
};
|
};
|
||||||
|
|
@ -7,8 +10,10 @@ use core::{
|
||||||
use alloc::{
|
use alloc::{
|
||||||
alloc::{Allocator, Global},
|
alloc::{Allocator, Global},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
|
sync::Arc,
|
||||||
vec,
|
vec,
|
||||||
};
|
};
|
||||||
|
use bytemuck::{AnyBitPattern, NoUninit};
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
channel::{Channel, TrySendError},
|
channel::{Channel, TrySendError},
|
||||||
signal::Signal,
|
signal::Signal,
|
||||||
|
|
@ -20,7 +25,7 @@ use esp_hal::{
|
||||||
dma::{
|
dma::{
|
||||||
self, AnyGdmaChannel, BufView, BurstConfig, DmaChannel, DmaChannelConvert, DmaDescriptor,
|
self, AnyGdmaChannel, BufView, BurstConfig, DmaChannel, DmaChannelConvert, DmaDescriptor,
|
||||||
DmaDescriptorFlags, DmaEligible, DmaRxStreamBuf, DmaTxBuf, DmaTxBuffer, DmaTxInterrupt,
|
DmaDescriptorFlags, DmaEligible, DmaRxStreamBuf, DmaTxBuf, DmaTxBuffer, DmaTxInterrupt,
|
||||||
ExternalBurstConfig, InternalBurstConfig, Mem2Mem, SimpleMem2MemTransfer,
|
ExternalBurstConfig, InternalBurstConfig, Mem2Mem, SimpleMem2Mem, SimpleMem2MemTransfer,
|
||||||
},
|
},
|
||||||
dma_descriptors, handler,
|
dma_descriptors, handler,
|
||||||
interrupt::{self, Priority},
|
interrupt::{self, Priority},
|
||||||
|
|
@ -33,8 +38,9 @@ use esp_sync::RawMutex;
|
||||||
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
|
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
|
||||||
use indoc::{formatdoc, indoc};
|
use indoc::{formatdoc, indoc};
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
|
use ouroboros::self_referencing;
|
||||||
use rmk::{
|
use rmk::{
|
||||||
futures::{FutureExt, pin_mut},
|
futures::{self, FutureExt, pin_mut},
|
||||||
join_all,
|
join_all,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -76,6 +82,125 @@ pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) {
|
||||||
const DMA_CHANNEL_OUTBOUND: usize = 2;
|
const DMA_CHANNEL_OUTBOUND: usize = 2;
|
||||||
const INTERRUPT_OUTBOUND: Interrupt = Interrupt::DMA_OUT_CH2;
|
const INTERRUPT_OUTBOUND: Interrupt = Interrupt::DMA_OUT_CH2;
|
||||||
|
|
||||||
|
#[self_referencing]
|
||||||
|
struct ReceivingTransfer {
|
||||||
|
mem2mem: SimpleMem2Mem<'static, Blocking>,
|
||||||
|
#[borrows(mut mem2mem)]
|
||||||
|
#[covariant]
|
||||||
|
transfer: Option<SimpleMem2MemTransfer<'this, 'static, Blocking>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Swapchain {
|
||||||
|
pub framebuffers: [&'static mut [u8]; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Swapchain {
|
||||||
|
pub fn into_reader_writer(self) -> (SwapchainReader, SwapchainWriter) {
|
||||||
|
assert_eq!(
|
||||||
|
self.framebuffers[0].len(),
|
||||||
|
self.framebuffers[1].len(),
|
||||||
|
"framebuffers in a swapchain must have an equal length"
|
||||||
|
);
|
||||||
|
|
||||||
|
let reader_index = Arc::new(AtomicBool::new(true));
|
||||||
|
|
||||||
|
(
|
||||||
|
SwapchainReader {
|
||||||
|
framebuffers_rw: [
|
||||||
|
self.framebuffers[0] as *const [u8],
|
||||||
|
self.framebuffers[1] as *const [u8],
|
||||||
|
],
|
||||||
|
reader_index: reader_index.clone(),
|
||||||
|
},
|
||||||
|
SwapchainWriter {
|
||||||
|
framebuffers_wr: [
|
||||||
|
self.framebuffers[1] as *mut [u8],
|
||||||
|
self.framebuffers[0] as *mut [u8],
|
||||||
|
],
|
||||||
|
reader_index,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Don't need to store the framebuffer length twice. Use `*const u8` instead, and store length separately.
|
||||||
|
pub struct SwapchainReader {
|
||||||
|
/// These are in the opposite order to `SwapchainWriter`'s framebuffers.
|
||||||
|
framebuffers_rw: [*const [u8]; 2],
|
||||||
|
reader_index: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for SwapchainReader {}
|
||||||
|
|
||||||
|
impl SwapchainReader {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.framebuffers_rw[0].len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_read_index(&self) -> usize {
|
||||||
|
self.reader_index.load(atomic::Ordering::SeqCst) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_latest_framebuffer(&self) -> &[u8] {
|
||||||
|
unsafe { &*self.framebuffers_rw[self.load_read_index()] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Don't need to store the framebuffer length twice. Use `*mut u8` instead, and store length separately.
|
||||||
|
pub struct SwapchainWriter {
|
||||||
|
/// These are in the opposite order to `SwapchainReader`'s framebuffers.
|
||||||
|
framebuffers_wr: [*mut [u8]; 2],
|
||||||
|
reader_index: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for SwapchainWriter {}
|
||||||
|
|
||||||
|
impl SwapchainWriter {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.framebuffers_wr[0].len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self) -> SwapchainWriteGuard<'_> {
|
||||||
|
let framebuffer_ptr =
|
||||||
|
self.framebuffers_wr[self.reader_index.load(atomic::Ordering::SeqCst) as usize];
|
||||||
|
SwapchainWriteGuard {
|
||||||
|
framebuffer: unsafe { &mut *framebuffer_ptr },
|
||||||
|
reader_index: &self.reader_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SwapchainWriteGuard<'a> {
|
||||||
|
framebuffer: &'a mut [u8],
|
||||||
|
reader_index: &'a AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SwapchainWriteGuard<'a> {
|
||||||
|
pub fn cast<T: NoUninit + AnyBitPattern>(&mut self) -> &mut [T] {
|
||||||
|
bytemuck::cast_slice_mut::<_, T>(&mut self.framebuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SwapchainWriteGuard<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.reader_index.fetch_xor(true, atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for SwapchainWriteGuard<'a> {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.framebuffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for SwapchainWriteGuard<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.framebuffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DmaBounce {
|
pub struct DmaBounce {
|
||||||
// TODO: Make these generic.
|
// TODO: Make these generic.
|
||||||
// They currently cannot be generic, because they lacks a `reborrow` method.
|
// They currently cannot be generic, because they lacks a `reborrow` method.
|
||||||
|
|
@ -84,6 +209,8 @@ pub struct DmaBounce {
|
||||||
peripheral_src: AnySpi<'static>,
|
peripheral_src: AnySpi<'static>,
|
||||||
// This can also be more generic, see `DmaEligible` in `Mem2Mem::new`.
|
// This can also be more generic, see `DmaEligible` in `Mem2Mem::new`.
|
||||||
peripheral_dst: Option<Dpi<'static, Blocking>>,
|
peripheral_dst: Option<Dpi<'static, Blocking>>,
|
||||||
|
// TODO: Combine with peripheral_dst using an enum?
|
||||||
|
transfer_dst: Option<DpiTransfer<'static, DmaTxBounceBuf, Blocking>>,
|
||||||
|
|
||||||
// TODO: Consider having a separate burst config for the two transfers.
|
// TODO: Consider having a separate burst config for the two transfers.
|
||||||
burst_config: BurstConfig,
|
burst_config: BurstConfig,
|
||||||
|
|
@ -92,7 +219,7 @@ pub struct DmaBounce {
|
||||||
window_size: usize,
|
window_size: usize,
|
||||||
/// The number of windows.
|
/// The number of windows.
|
||||||
windows_len: usize,
|
windows_len: usize,
|
||||||
buffer_src: &'static mut [u8],
|
swapchain_src: SwapchainReader,
|
||||||
// Two buffers of size `window_size`,
|
// Two buffers of size `window_size`,
|
||||||
// one of which is being written to, while the other is being read from.
|
// one of which is being written to, while the other is being read from.
|
||||||
bounce_buffer_dst: &'static mut [u8],
|
bounce_buffer_dst: &'static mut [u8],
|
||||||
|
|
@ -105,9 +232,11 @@ pub struct DmaBounce {
|
||||||
bounce_dst_descs: &'static mut [DmaDescriptor],
|
bounce_dst_descs: &'static mut [DmaDescriptor],
|
||||||
// A cyclic descriptor list that spans the buffers `bounce_buffer_dst` and `bounce_buffer_src`.
|
// A cyclic descriptor list that spans the buffers `bounce_buffer_dst` and `bounce_buffer_src`.
|
||||||
bounce_src_descs: &'static mut [DmaDescriptor],
|
bounce_src_descs: &'static mut [DmaDescriptor],
|
||||||
|
descriptors_per_window: usize,
|
||||||
// The index of the next window about to be received into the destination bounce buffer.
|
// The index of the next window about to be received into the destination bounce buffer.
|
||||||
window_index_next: usize,
|
window_index_next: usize,
|
||||||
frame_index_next: usize,
|
frame_index_next: usize,
|
||||||
|
receiving_transfer: Option<ReceivingTransfer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DmaBounce {
|
impl DmaBounce {
|
||||||
|
|
@ -126,7 +255,7 @@ impl DmaBounce {
|
||||||
channel: DMA_CH0<'static>,
|
channel: DMA_CH0<'static>,
|
||||||
peripheral_src: AnySpi<'static>,
|
peripheral_src: AnySpi<'static>,
|
||||||
peripheral_dst: Dpi<'static, Blocking>,
|
peripheral_dst: Dpi<'static, Blocking>,
|
||||||
buffer_src: &'static mut [u8],
|
swapchain_src: SwapchainReader,
|
||||||
row_front_porch_bytes: usize,
|
row_front_porch_bytes: usize,
|
||||||
row_width_bytes: usize,
|
row_width_bytes: usize,
|
||||||
window_size_rows: usize,
|
window_size_rows: usize,
|
||||||
|
|
@ -136,20 +265,23 @@ impl DmaBounce {
|
||||||
let window_size = row_width_bytes * window_size_rows;
|
let window_size = row_width_bytes * window_size_rows;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer_src.len() % window_size,
|
swapchain_src.len() % window_size,
|
||||||
0,
|
0,
|
||||||
"the size of a source buffer must be a multiple of the window size ({window_size} bytes), but it is {len} bytes large",
|
"the size of a source buffer must be a multiple of the window size ({window_size} bytes), but it is {len} bytes large",
|
||||||
len = buffer_src.len()
|
len = swapchain_src.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Conservative alignment. Maxiumum of the cartesian product of [tx, rx] × [internal, external].
|
// Conservative alignment. Maxiumum of the cartesian product of [tx, rx] × [internal, external].
|
||||||
let alignment = burst_config.min_compatible_alignment();
|
let alignment = burst_config.min_compatible_alignment();
|
||||||
|
|
||||||
|
for &swapchain_ptr in &swapchain_src.framebuffers_rw {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer_src.as_ptr() as usize % alignment,
|
unsafe { &*swapchain_ptr }.as_ptr() as usize % alignment,
|
||||||
0,
|
0,
|
||||||
"the source buffer must be sufficiently aligned to {alignment} bytes for the burst config",
|
"the source buffer must be sufficiently aligned to {alignment} bytes for the burst config",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
row_width_bytes % alignment,
|
row_width_bytes % alignment,
|
||||||
0,
|
0,
|
||||||
|
|
@ -168,7 +300,7 @@ impl DmaBounce {
|
||||||
"front porch too large"
|
"front porch too large"
|
||||||
);
|
);
|
||||||
|
|
||||||
let windows_len = buffer_src.len() / window_size;
|
let windows_len = swapchain_src.len() / window_size;
|
||||||
// TODO: Figure out a way to avoid `leak`ing memory.
|
// TODO: Figure out a way to avoid `leak`ing memory.
|
||||||
// We probably want to store the `Box`es and then unsafely extend the lifetime at sites of usage.
|
// We probably want to store the `Box`es and then unsafely extend the lifetime at sites of usage.
|
||||||
let bounce_buffer_dst =
|
let bounce_buffer_dst =
|
||||||
|
|
@ -183,21 +315,7 @@ impl DmaBounce {
|
||||||
});
|
});
|
||||||
let bounce_dst_descs =
|
let bounce_dst_descs =
|
||||||
Self::linear_descriptors_for_buffer(window_size, burst_config, |_| {});
|
Self::linear_descriptors_for_buffer(window_size, burst_config, |_| {});
|
||||||
let bounce_src_descs = if cyclic {
|
let (bounce_src_descs, descriptors_per_window) = Self::bounce_descriptors_for_buffer(
|
||||||
Self::bounce_descriptors_for_buffer_cyclic(
|
|
||||||
row_front_porch_bytes,
|
|
||||||
row_width_bytes,
|
|
||||||
window_size_rows,
|
|
||||||
unsafe {
|
|
||||||
(
|
|
||||||
&mut *(bounce_buffer_dst as *mut _),
|
|
||||||
&mut *(bounce_buffer_src as *mut _),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
burst_config,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Self::bounce_descriptors_for_buffer_single(
|
|
||||||
windows_len,
|
windows_len,
|
||||||
row_front_porch_bytes,
|
row_front_porch_bytes,
|
||||||
row_width_bytes,
|
row_width_bytes,
|
||||||
|
|
@ -209,25 +327,28 @@ impl DmaBounce {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
burst_config,
|
burst_config,
|
||||||
)
|
cyclic,
|
||||||
};
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
channel,
|
channel,
|
||||||
peripheral_src,
|
peripheral_src,
|
||||||
peripheral_dst: Some(peripheral_dst),
|
peripheral_dst: Some(peripheral_dst),
|
||||||
|
transfer_dst: None,
|
||||||
burst_config,
|
burst_config,
|
||||||
cyclic,
|
cyclic,
|
||||||
window_size,
|
window_size,
|
||||||
windows_len,
|
windows_len,
|
||||||
buffer_src,
|
swapchain_src,
|
||||||
bounce_buffer_dst,
|
bounce_buffer_dst,
|
||||||
bounce_buffer_src,
|
bounce_buffer_src,
|
||||||
src_descs,
|
src_descs,
|
||||||
bounce_dst_descs,
|
bounce_dst_descs,
|
||||||
bounce_src_descs,
|
bounce_src_descs,
|
||||||
|
descriptors_per_window,
|
||||||
window_index_next: 0,
|
window_index_next: 0,
|
||||||
frame_index_next: 0,
|
frame_index_next: 0,
|
||||||
|
receiving_transfer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -358,81 +479,15 @@ impl DmaBounce {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounce_descriptors_for_buffer_cyclic(
|
fn bounce_descriptors_for_buffer(
|
||||||
row_front_porch_bytes: usize,
|
|
||||||
row_width_bytes: usize,
|
|
||||||
window_size_rows: usize,
|
|
||||||
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
|
|
||||||
burst_config: BurstConfig,
|
|
||||||
) -> &'static mut [DmaDescriptor] {
|
|
||||||
assert_eq!(
|
|
||||||
bounce_buffers.0.len(),
|
|
||||||
bounce_buffers.1.len(),
|
|
||||||
"bounce buffers must be equal in size"
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffer_len = bounce_buffers.0.len();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
buffer_len,
|
|
||||||
row_width_bytes * window_size_rows,
|
|
||||||
"the provided bounce buffers have an invalid size"
|
|
||||||
);
|
|
||||||
|
|
||||||
let max_chunk_size = burst_config.max_compatible_chunk_size();
|
|
||||||
let descriptors_per_row_front_porch =
|
|
||||||
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
|
|
||||||
let descriptors_per_row_stored =
|
|
||||||
dma::descriptor_count(row_width_bytes, max_chunk_size, false);
|
|
||||||
let descriptors_per_row = descriptors_per_row_stored + descriptors_per_row_front_porch;
|
|
||||||
let descriptors_per_window = window_size_rows * descriptors_per_row;
|
|
||||||
let descriptors_combined =
|
|
||||||
Box::leak(vec![DmaDescriptor::EMPTY; 2 * descriptors_per_window].into_boxed_slice());
|
|
||||||
let descriptors_pair = descriptors_combined.split_at_mut(descriptors_per_window);
|
|
||||||
|
|
||||||
// Link up the descriptors.
|
|
||||||
fn link_up_descriptors(
|
|
||||||
descriptors: &mut [DmaDescriptor],
|
|
||||||
descriptors_other: &mut [DmaDescriptor],
|
|
||||||
) {
|
|
||||||
let mut next = descriptors_other.first_mut().unwrap();
|
|
||||||
for desc in descriptors.iter_mut().rev() {
|
|
||||||
desc.next = next;
|
|
||||||
next = desc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
link_up_descriptors(descriptors_pair.0, descriptors_pair.1);
|
|
||||||
link_up_descriptors(descriptors_pair.1, descriptors_pair.0);
|
|
||||||
|
|
||||||
// Prepare each descriptor's buffer size.
|
|
||||||
for (bounce_buffer, descriptors) in [
|
|
||||||
(bounce_buffers.0, descriptors_pair.0),
|
|
||||||
(bounce_buffers.1, descriptors_pair.1),
|
|
||||||
] {
|
|
||||||
Self::prepare_descriptors_window(
|
|
||||||
bounce_buffer,
|
|
||||||
descriptors,
|
|
||||||
row_front_porch_bytes,
|
|
||||||
row_width_bytes,
|
|
||||||
window_size_rows,
|
|
||||||
max_chunk_size,
|
|
||||||
descriptors_per_row,
|
|
||||||
descriptors_per_row_front_porch,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptors_combined
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounce_descriptors_for_buffer_single(
|
|
||||||
windows_len: usize,
|
windows_len: usize,
|
||||||
row_front_porch_bytes: usize,
|
row_front_porch_bytes: usize,
|
||||||
row_width_bytes: usize,
|
row_width_bytes: usize,
|
||||||
window_size_rows: usize,
|
window_size_rows: usize,
|
||||||
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
|
bounce_buffers: (&'static mut [u8], &'static mut [u8]),
|
||||||
burst_config: BurstConfig,
|
burst_config: BurstConfig,
|
||||||
) -> &'static mut [DmaDescriptor] {
|
cyclic: bool,
|
||||||
|
) -> (&'static mut [DmaDescriptor], usize) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bounce_buffers.0.len(),
|
bounce_buffers.0.len(),
|
||||||
bounce_buffers.1.len(),
|
bounce_buffers.1.len(),
|
||||||
|
|
@ -449,6 +504,15 @@ impl DmaBounce {
|
||||||
"the provided bounce buffers have an invalid size"
|
"the provided bounce buffers have an invalid size"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Implementation note:
|
||||||
|
// A cyclic descriptor could consist of just a set of descriptors per window,
|
||||||
|
// so two sets in total, because there are two bounce buffers.
|
||||||
|
// However, we can also access the pointer of the EOF descriptor within the
|
||||||
|
// EOF interrupt handler, which lets us compute which descriptor generated that
|
||||||
|
// interrupt.
|
||||||
|
// This is useful in the case when an interrupt is missed. Then the number of interrupts
|
||||||
|
// handled doesn't correspond to the number of windows sent to the destination peripheral.
|
||||||
|
// In that case, the number of windows sent can be computed from the address of the descriptor.
|
||||||
let max_chunk_size = burst_config.max_compatible_chunk_size();
|
let max_chunk_size = burst_config.max_compatible_chunk_size();
|
||||||
let descriptors_per_row_front_porch =
|
let descriptors_per_row_front_porch =
|
||||||
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
|
dma::descriptor_count(row_front_porch_bytes, max_chunk_size, false);
|
||||||
|
|
@ -459,9 +523,15 @@ impl DmaBounce {
|
||||||
let descriptors_per_frame = descriptors_per_window * windows_len;
|
let descriptors_per_frame = descriptors_per_window * windows_len;
|
||||||
let descriptors_frame =
|
let descriptors_frame =
|
||||||
Box::leak(vec![DmaDescriptor::EMPTY; descriptors_per_frame].into_boxed_slice());
|
Box::leak(vec![DmaDescriptor::EMPTY; descriptors_per_frame].into_boxed_slice());
|
||||||
|
let descriptors_frame_ptr = descriptors_frame.as_ptr();
|
||||||
|
|
||||||
// Link up the descriptors.
|
// Link up the descriptors.
|
||||||
let mut next = core::ptr::null_mut();
|
let mut next = if cyclic {
|
||||||
|
descriptors_frame.first_mut().unwrap() as *mut _
|
||||||
|
} else {
|
||||||
|
core::ptr::null_mut()
|
||||||
|
};
|
||||||
|
|
||||||
for desc in descriptors_frame.iter_mut().rev() {
|
for desc in descriptors_frame.iter_mut().rev() {
|
||||||
desc.next = next;
|
desc.next = next;
|
||||||
next = desc;
|
next = desc;
|
||||||
|
|
@ -497,10 +567,31 @@ impl DmaBounce {
|
||||||
windows_len * window_size_rows * (row_front_porch_bytes + row_width_bytes)
|
windows_len * window_size_rows * (row_front_porch_bytes + row_width_bytes)
|
||||||
);
|
);
|
||||||
|
|
||||||
descriptors_frame
|
(descriptors_frame, descriptors_per_window)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linear_descriptors_prepare(
|
fn linear_descriptors_prepare(
|
||||||
|
descriptors: &mut [DmaDescriptor],
|
||||||
|
mut buffer: Option<&[u8]>,
|
||||||
|
mut setup_desc: impl FnMut(&mut DmaDescriptor),
|
||||||
|
) {
|
||||||
|
for descriptor in descriptors.iter_mut() {
|
||||||
|
if let Some(inner_buffer) = buffer {
|
||||||
|
descriptor.buffer = inner_buffer.as_ptr() as *mut u8;
|
||||||
|
buffer = Some(&inner_buffer[descriptor.size()..]);
|
||||||
|
}
|
||||||
|
(setup_desc)(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(buffer) = buffer {
|
||||||
|
assert!(
|
||||||
|
buffer.is_empty(),
|
||||||
|
"a buffer of an incompatible length was assigned to a descriptor set"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linear_descriptors_prepare_mut(
|
||||||
descriptors: &mut [DmaDescriptor],
|
descriptors: &mut [DmaDescriptor],
|
||||||
mut buffer: Option<&mut [u8]>,
|
mut buffer: Option<&mut [u8]>,
|
||||||
mut setup_desc: impl FnMut(&mut DmaDescriptor),
|
mut setup_desc: impl FnMut(&mut DmaDescriptor),
|
||||||
|
|
@ -551,10 +642,13 @@ impl DmaBounce {
|
||||||
|
|
||||||
/// Receive a window of bytes into the current dst bounce buffer.
|
/// Receive a window of bytes into the current dst bounce buffer.
|
||||||
/// Finally, swaps the bounce buffers.
|
/// Finally, swaps the bounce buffers.
|
||||||
async fn receive_window(&mut self) {
|
///
|
||||||
|
/// # Safety:
|
||||||
|
/// TODO
|
||||||
|
unsafe fn receive_window_start(&mut self) -> ReceivingTransfer {
|
||||||
// Descriptors are initialized by `DmaTxBuf::new`.
|
// Descriptors are initialized by `DmaTxBuf::new`.
|
||||||
let buffer_src_window =
|
let buffer_src_window = &self.swapchain_src.get_latest_framebuffer()
|
||||||
&mut self.buffer_src[self.window_index_next * self.window_size..][..self.window_size];
|
[self.window_index_next * self.window_size..][..self.window_size];
|
||||||
|
|
||||||
Self::linear_descriptors_prepare(self.src_descs, Some(buffer_src_window), |_desc| {
|
Self::linear_descriptors_prepare(self.src_descs, Some(buffer_src_window), |_desc| {
|
||||||
// No need to call `DmaDescriptor::reset_for_tx`, because
|
// No need to call `DmaDescriptor::reset_for_tx`, because
|
||||||
|
|
@ -562,7 +656,59 @@ impl DmaBounce {
|
||||||
// 2. the EOF flag is already set during the construction of this buffer.
|
// 2. the EOF flag is already set during the construction of this buffer.
|
||||||
});
|
});
|
||||||
// TODO: Precompute a descriptor list for each buffer, then use `None` instead of `Some(&mut *self.bounce_buffer_dst)`.
|
// TODO: Precompute a descriptor list for each buffer, then use `None` instead of `Some(&mut *self.bounce_buffer_dst)`.
|
||||||
Self::linear_descriptors_prepare(
|
Self::linear_descriptors_prepare_mut(
|
||||||
|
self.bounce_dst_descs,
|
||||||
|
Some(&mut *self.bounce_buffer_dst),
|
||||||
|
|desc| {
|
||||||
|
desc.reset_for_rx();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extend the lifetime to 'static because it is required by Mem2Mem.
|
||||||
|
//
|
||||||
|
// Safety:
|
||||||
|
// Pointees are done being used by the driver before this scope ends,
|
||||||
|
// this is because we `SimpleMem2MemTransfer::wait()` on the transfer to finish.
|
||||||
|
let bounce_dst_descs: &'static mut [DmaDescriptor] =
|
||||||
|
unsafe { &mut *(self.bounce_dst_descs as *mut _) };
|
||||||
|
let src_descs: &'static mut [DmaDescriptor] = unsafe { &mut *(self.src_descs as *mut _) };
|
||||||
|
|
||||||
|
let mem2mem = unsafe {
|
||||||
|
Mem2Mem::new(
|
||||||
|
self.channel.clone_unchecked(),
|
||||||
|
self.peripheral_src.clone_unchecked(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.with_descriptors(bounce_dst_descs, src_descs, self.burst_config)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
ReceivingTransferBuilder {
|
||||||
|
mem2mem,
|
||||||
|
transfer_builder: |mem2mem| {
|
||||||
|
Some(
|
||||||
|
mem2mem
|
||||||
|
.start_transfer(&mut self.bounce_buffer_dst, buffer_src_window)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive a window of bytes into the current dst bounce buffer.
|
||||||
|
/// Finally, swaps the bounce buffers.
|
||||||
|
async fn receive_window(&mut self) {
|
||||||
|
// Descriptors are initialized by `DmaTxBuf::new`.
|
||||||
|
let buffer_src_window = &self.swapchain_src.get_latest_framebuffer()
|
||||||
|
[self.window_index_next * self.window_size..][..self.window_size];
|
||||||
|
|
||||||
|
Self::linear_descriptors_prepare(self.src_descs, Some(buffer_src_window), |_desc| {
|
||||||
|
// No need to call `DmaDescriptor::reset_for_tx`, because
|
||||||
|
// 1. we don't rely on the ownership flag;
|
||||||
|
// 2. the EOF flag is already set during the construction of this buffer.
|
||||||
|
});
|
||||||
|
// TODO: Precompute a descriptor list for each buffer, then use `None` instead of `Some(&mut *self.bounce_buffer_dst)`.
|
||||||
|
Self::linear_descriptors_prepare_mut(
|
||||||
self.bounce_dst_descs,
|
self.bounce_dst_descs,
|
||||||
Some(&mut *self.bounce_buffer_dst),
|
Some(&mut *self.bounce_buffer_dst),
|
||||||
|desc| {
|
|desc| {
|
||||||
|
|
@ -595,14 +741,39 @@ impl DmaBounce {
|
||||||
self.increase_window_counter(1);
|
self.increase_window_counter(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increase_window_counter(&mut self, windows: usize) {
|
fn increase_window_counter(&mut self, windows: isize) {
|
||||||
if windows % 2 == 1 {
|
if windows.rem_euclid(2) == 1 {
|
||||||
core::mem::swap(&mut self.bounce_buffer_dst, &mut self.bounce_buffer_src);
|
core::mem::swap(&mut self.bounce_buffer_dst, &mut self.bounce_buffer_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.window_index_next += windows;
|
let window_index_next = self.window_index_next as isize + windows;
|
||||||
self.frame_index_next += self.window_index_next / self.windows_len;
|
self.frame_index_next = (self.frame_index_next as isize
|
||||||
self.window_index_next = self.window_index_next % self.windows_len;
|
+ window_index_next / self.windows_len as isize)
|
||||||
|
as usize;
|
||||||
|
self.window_index_next = window_index_next.rem_euclid(self.windows_len as isize) as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn launch_interrupt_driven_task(mut self) {
|
||||||
|
Self::enable_interrupts();
|
||||||
|
|
||||||
|
// Receive the first 2 windows, so that the outbound transfer can read valid data.
|
||||||
|
self.receive_window().await;
|
||||||
|
|
||||||
|
let dma_tx_buffer = self.get_dma_tx_buffer();
|
||||||
|
let transfer = self
|
||||||
|
.peripheral_dst
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.send(self.cyclic /* Send perpetually */, dma_tx_buffer)
|
||||||
|
.unwrap_or_else(|(error, _, _)| {
|
||||||
|
panic!("failed to begin the transmission of the first frame: {error:?}");
|
||||||
|
});
|
||||||
|
self.transfer_dst = Some(transfer);
|
||||||
|
self.receiving_transfer = Some(unsafe { self.receive_window_start() });
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*DMA_STATE.0.get() = Some(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&mut self) {
|
pub async fn send(&mut self) {
|
||||||
|
|
@ -633,31 +804,17 @@ impl DmaBounce {
|
||||||
// "Window received: {} {}",
|
// "Window received: {} {}",
|
||||||
// self.window_index_next, self.frame_index_next
|
// self.window_index_next, self.frame_index_next
|
||||||
// );
|
// );
|
||||||
let windows_skipped = WINDOWS_SKIPPED
|
let windows_sent = WINDOWS_SENT
|
||||||
.wait()
|
.wait()
|
||||||
.with_timeout(Duration::from_millis(100))
|
.with_timeout(Duration::from_millis(100))
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
error!("Timed out when waiting for skipped windows.");
|
error!("Timed out when waiting for skipped windows.");
|
||||||
0 // TODO: This should be -1 to repeat the same window.
|
0
|
||||||
});
|
});
|
||||||
|
let windows_skipped = windows_sent as isize - 1;
|
||||||
|
|
||||||
// let windows_skipped = match windows_skipped {
|
if windows_skipped != 0 {
|
||||||
// Ok(windows_skipped) => windows_skipped,
|
|
||||||
// Err(_) => {
|
|
||||||
// warn!(
|
|
||||||
// "Waiting for skipped windows timed out. Transfer done: {}",
|
|
||||||
// transfer.is_done()
|
|
||||||
// );
|
|
||||||
// if transfer.is_done() {
|
|
||||||
// let (result, _, _) = transfer.wait();
|
|
||||||
// panic!("Transfer result: {result:?}");
|
|
||||||
// }
|
|
||||||
// 0
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
if windows_skipped > 0 {
|
|
||||||
self.increase_window_counter(windows_skipped);
|
self.increase_window_counter(windows_skipped);
|
||||||
windows_skipped_total += windows_skipped;
|
windows_skipped_total += windows_skipped;
|
||||||
// error!(
|
// error!(
|
||||||
|
|
@ -674,7 +831,9 @@ impl DmaBounce {
|
||||||
|
|
||||||
if !self.cyclic && (self.window_index_next == 1 || transfer.is_done()) {
|
if !self.cyclic && (self.window_index_next == 1 || transfer.is_done()) {
|
||||||
if self.window_index_next > 1 {
|
if self.window_index_next > 1 {
|
||||||
self.increase_window_counter(self.windows_len - self.window_index_next + 1);
|
self.increase_window_counter(
|
||||||
|
self.windows_len as isize - self.window_index_next as isize + 1,
|
||||||
|
);
|
||||||
} else if self.window_index_next == 0 {
|
} else if self.window_index_next == 0 {
|
||||||
self.increase_window_counter(1);
|
self.increase_window_counter(1);
|
||||||
}
|
}
|
||||||
|
|
@ -763,25 +922,94 @@ unsafe impl DmaTxBuffer for DmaTxBounceBuf {
|
||||||
|
|
||||||
/// Intended to be listened on by the renderer, to synchronize the refresh frequency with.
|
/// Intended to be listened on by the renderer, to synchronize the refresh frequency with.
|
||||||
pub static FRAMES_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
pub static FRAMES_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
||||||
static WINDOWS_SKIPPED: Signal<RawMutex, usize> = Signal::new();
|
static WINDOWS_SENT: Signal<RawMutex, usize> = Signal::new();
|
||||||
// static INBOUND_TRANSFER_FINISHED: Signal<RawMutex, ()> = Signal::new();
|
static DMA_STATE: SyncUnsafeCell<Option<DmaBounce>> = SyncUnsafeCell(UnsafeCell::new(None));
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct SyncUnsafeCell<T>(UnsafeCell<T>);
|
||||||
|
|
||||||
|
unsafe impl<T> Sync for SyncUnsafeCell<T> {}
|
||||||
|
|
||||||
|
// #[derive(Clone, Copy)]
|
||||||
|
// struct DmaState {
|
||||||
|
// descriptors_ptr: *const DmaDescriptor,
|
||||||
|
// descriptors_per_window: usize,
|
||||||
|
// windows_per_frame: usize,
|
||||||
|
// last_window_index: usize,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// unsafe impl Sync for DmaState {}
|
||||||
|
|
||||||
#[handler(priority = Priority::Priority3)]
|
#[handler(priority = Priority::Priority3)]
|
||||||
#[ram] // Improves performance.
|
#[ram] // Improves performance.
|
||||||
fn dma_outbound_interrupt_handler() {
|
fn dma_outbound_interrupt_handler() {
|
||||||
let interrupt = DMA::regs().ch(DMA_CHANNEL_OUTBOUND).out_int();
|
let interrupt = DMA::regs().ch(DMA_CHANNEL_OUTBOUND).out_int();
|
||||||
let bounce_buffer_processed = interrupt.st().read().out_eof().bit_is_set();
|
let bounce_buffer_sent = interrupt.st().read().out_eof().bit_is_set();
|
||||||
if bounce_buffer_processed {
|
|
||||||
|
if !bounce_buffer_sent {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the bit by writing 1 to the clear bits.
|
// Clear the bit by writing 1 to the clear bits.
|
||||||
interrupt.clr().write(|w| w.out_eof().bit(true));
|
interrupt.clr().write(|w| w.out_eof().bit(true));
|
||||||
|
|
||||||
WINDOWS_SKIPPED.signal(
|
// SAFETY: This value is only ever read in our interrupt handler,
|
||||||
WINDOWS_SKIPPED
|
// and interrupts are disabled, and we only use this in one thread.
|
||||||
.try_take()
|
let Some(dma_state) = unsafe { &mut *DMA_STATE.0.get() }.as_mut() else {
|
||||||
.map(|windows_skipped| windows_skipped + 1)
|
error!("no DMA state available when executing DMA interrupt handler");
|
||||||
.unwrap_or_default(),
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The descriptor of the buffer with an EOF flag that just finished being sent.
|
||||||
|
let descriptor_ptr = DMA::regs()
|
||||||
|
.ch(DMA_CHANNEL_OUTBOUND)
|
||||||
|
.out_eof_des_addr()
|
||||||
|
.read()
|
||||||
|
.out_eof_des_addr()
|
||||||
|
.bits() as *const DmaDescriptor;
|
||||||
|
// This is the index of the window that just finished being transmitted to the destination peripheral.
|
||||||
|
let window_sent_index =
|
||||||
|
unsafe { descriptor_ptr.offset_from_unsigned(dma_state.bounce_src_descs.as_ptr()) }
|
||||||
|
/ dma_state.descriptors_per_window;
|
||||||
|
// warn!("{window_sent_index}");
|
||||||
|
// The next window to be sent is `(window_sent_index + 1) % dma_state.windows_len`.
|
||||||
|
// That is not the window we want to buffer, because the transmissions would race.
|
||||||
|
// We instead want to buffer the next window:
|
||||||
|
let window_index_next = (window_sent_index + 2) % dma_state.windows_len;
|
||||||
|
|
||||||
|
// Swap bounce buffers.
|
||||||
|
if (dma_state.windows_len + window_index_next - dma_state.window_index_next) % 2 == 1 {
|
||||||
|
core::mem::swap(
|
||||||
|
&mut dma_state.bounce_buffer_dst,
|
||||||
|
&mut dma_state.bounce_buffer_src,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dma_state.window_index_next = window_index_next;
|
||||||
|
|
||||||
|
// warn!("{window_sent_index} {window_index_next}");
|
||||||
|
|
||||||
|
let mut receiving_transfer = dma_state
|
||||||
|
.receiving_transfer
|
||||||
|
.take()
|
||||||
|
.expect("no ongoing transfer to a bounce buffer present");
|
||||||
|
let receiving_transfer = receiving_transfer
|
||||||
|
.with_mut(|x| x.transfer.take())
|
||||||
|
.expect("no ongoing inner transfer to a bounce buffer present");
|
||||||
|
|
||||||
|
// if !receiving_transfer.is_done() {
|
||||||
|
// error!("{window_sent_index}");
|
||||||
|
// error!("the transfer to a bounce buffer has not finished yet, aborting");
|
||||||
|
// }
|
||||||
|
|
||||||
|
if receiving_transfer.is_done() {
|
||||||
|
drop(receiving_transfer);
|
||||||
|
} else {
|
||||||
|
receiving_transfer.wait().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is any ongoing transfer, cancel it and start a new one.
|
||||||
|
dma_state.receiving_transfer = Some(unsafe { dma_state.receive_window_start() });
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[handler(priority = Priority::Priority3)]
|
// #[handler(priority = Priority::Priority3)]
|
||||||
|
|
@ -879,10 +1107,12 @@ pub fn allocate_dma_buffer_in<A: Allocator>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename or get rid of.
|
||||||
pub struct Framebuffer {
|
pub struct Framebuffer {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
pub bounce_buffers: DmaBounce,
|
pub swapchain: Option<SwapchainWriter>,
|
||||||
|
pub bounce_buffers: Option<DmaBounce>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Framebuffer {
|
impl Framebuffer {
|
||||||
|
|
@ -899,17 +1129,25 @@ impl Framebuffer {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
||||||
let buffer_size = width_pixels as usize * height_pixels as usize * BYTES_PER_PIXEL;
|
let buffer_size = width_pixels as usize * height_pixels as usize * BYTES_PER_PIXEL;
|
||||||
let psram_buffer = Box::leak(allocate_dma_buffer_in(
|
let framebuffers = [
|
||||||
|
Box::leak(allocate_dma_buffer_in(
|
||||||
buffer_size,
|
buffer_size,
|
||||||
burst_config,
|
burst_config,
|
||||||
&PSRAM_ALLOCATOR,
|
&PSRAM_ALLOCATOR,
|
||||||
));
|
)),
|
||||||
|
Box::leak(allocate_dma_buffer_in(
|
||||||
|
buffer_size,
|
||||||
|
burst_config,
|
||||||
|
&PSRAM_ALLOCATOR,
|
||||||
|
)),
|
||||||
|
];
|
||||||
|
let (swapchain_reader, swapchain_writer) = Swapchain { framebuffers }.into_reader_writer();
|
||||||
let bounce_buffers = DmaBounce::new(
|
let bounce_buffers = DmaBounce::new(
|
||||||
Global,
|
Global,
|
||||||
channel,
|
channel,
|
||||||
peripheral_src,
|
peripheral_src,
|
||||||
peripheral_dst,
|
peripheral_dst,
|
||||||
psram_buffer,
|
swapchain_reader,
|
||||||
front_porch_pixels as usize * BYTES_PER_PIXEL,
|
front_porch_pixels as usize * BYTES_PER_PIXEL,
|
||||||
width_pixels as usize * BYTES_PER_PIXEL,
|
width_pixels as usize * BYTES_PER_PIXEL,
|
||||||
rows_per_window,
|
rows_per_window,
|
||||||
|
|
@ -920,11 +1158,8 @@ impl Framebuffer {
|
||||||
Self {
|
Self {
|
||||||
width: width_pixels,
|
width: width_pixels,
|
||||||
height: height_pixels,
|
height: height_pixels,
|
||||||
bounce_buffers,
|
swapchain: Some(swapchain_writer),
|
||||||
|
bounce_buffers: Some(bounce_buffers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_target_pixels(&mut self) -> &mut [Rgb565Pixel] {
|
|
||||||
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.bounce_buffers.buffer_src)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -377,11 +377,12 @@ impl State {
|
||||||
// SIGNAL_LCD_SUBMIT.signal(());
|
// SIGNAL_LCD_SUBMIT.signal(());
|
||||||
#[cfg(feature = "limit-fps")]
|
#[cfg(feature = "limit-fps")]
|
||||||
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
||||||
let frames_skipped = FRAMES_SKIPPED.wait().await;
|
// let frames_skipped = FRAMES_SKIPPED.wait().await;
|
||||||
|
|
||||||
|
// if frames_skipped > 0 {
|
||||||
|
// error!("Renderer missed {frames_skipped} frames.");
|
||||||
|
// }
|
||||||
|
|
||||||
if frames_skipped > 0 {
|
|
||||||
error!("Renderer missed {frames_skipped} frames.");
|
|
||||||
}
|
|
||||||
// SIGNAL_UI_RENDER.wait().await;
|
// SIGNAL_UI_RENDER.wait().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue