first commit, not pretty
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.direnv
|
||||||
818
Cargo.lock
generated
Normal file
818
Cargo.lock
generated
Normal file
@@ -0,0 +1,818 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dlib"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||||
|
dependencies = [
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.178"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minifb"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c470a74618b43cd182c21b3dc1e6123501249f3bad9a0085e95d1304ca2478"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"dlib",
|
||||||
|
"futures",
|
||||||
|
"instant",
|
||||||
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"orbclient",
|
||||||
|
"raw-window-handle",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"tempfile",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-cursor",
|
||||||
|
"wayland-protocols",
|
||||||
|
"winapi",
|
||||||
|
"x11-dl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.24.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orbclient"
|
||||||
|
version = "0.3.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"sdl2",
|
||||||
|
"sdl2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.16",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-window-handle"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl2"
|
||||||
|
version = "0.35.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"sdl2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl2-sys"
|
||||||
|
version = "0.35.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"version-compare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.111"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "teleprof"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"minifb",
|
||||||
|
"once_cell",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.1+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-client"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"downcast-rs",
|
||||||
|
"libc",
|
||||||
|
"nix",
|
||||||
|
"scoped-tls",
|
||||||
|
"wayland-commons",
|
||||||
|
"wayland-scanner",
|
||||||
|
"wayland-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-commons"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"once_cell",
|
||||||
|
"smallvec",
|
||||||
|
"wayland-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-cursor"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"wayland-client",
|
||||||
|
"xcursor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-protocols"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-commons",
|
||||||
|
"wayland-scanner",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-scanner"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-sys"
|
||||||
|
version = "0.29.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
|
||||||
|
dependencies = [
|
||||||
|
"dlib",
|
||||||
|
"lazy_static",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x11-dl"
|
||||||
|
version = "2.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xcursor"
|
||||||
|
version = "0.3.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xml-rs"
|
||||||
|
version = "0.8.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "teleprof"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
minifb = "0.27"
|
||||||
|
crossbeam-channel = "0.5"
|
||||||
|
once_cell = "1.19"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.8"
|
||||||
|
minifb = "0.27"
|
||||||
141
README.md
Normal file
141
README.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# Teleprof
|
||||||
|
|
||||||
|
A lightweight, debug-only telemetry profiler for Rust applications. Shows thread activity and call stack hierarchy in real-time.
|
||||||
|
|
||||||
|
Inspired by RAD Telemetry - built in ~400 LOC with minimal dependencies.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Icicle graph** showing call stack hierarchy (top half)
|
||||||
|
- **Thread timeline** showing per-thread activity over time (bottom half)
|
||||||
|
- **Monokai color palette** for easy visual distinction
|
||||||
|
- **Pause mechanism** to freeze your application for inspection
|
||||||
|
- **Ringbuffer storage** (~16MB, 1M events) for recent history
|
||||||
|
- **Lock-free event recording** via MPSC channels
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Only 3 dependencies (~15 total including transitive):
|
||||||
|
- `minifb` - Window and framebuffer
|
||||||
|
- `crossbeam-channel` - Lock-free MPSC
|
||||||
|
- `once_cell` - Lazy statics
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Add to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
teleprof = { path = "../teleprof" } # or from crates.io when published
|
||||||
|
```
|
||||||
|
|
||||||
|
### In your code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
// Start the profiler window (separate thread)
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
teleprof::start();
|
||||||
|
|
||||||
|
// Your application code
|
||||||
|
game_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn game_loop() {
|
||||||
|
loop {
|
||||||
|
// Profile a scope
|
||||||
|
teleprof::span!("game_loop");
|
||||||
|
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
|
||||||
|
// Check if paused (optional)
|
||||||
|
if teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
// Wait until unpaused
|
||||||
|
while teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update() {
|
||||||
|
teleprof::span!("update");
|
||||||
|
// Your update code
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render() {
|
||||||
|
teleprof::span!("render");
|
||||||
|
// Your render code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### For closures:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let work = || {
|
||||||
|
teleprof::span!("my_closure");
|
||||||
|
// work...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
- **Space**: Toggle pause (acquires `PAUSE` lock to freeze your app)
|
||||||
|
- **Escape**: Close profiler window
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
1. `span!()` macro creates a `SpanGuard` that sends `SpanStart` on creation
|
||||||
|
2. When the guard drops, sends `SpanEnd`
|
||||||
|
3. Events are sent via lock-free MPSC channel
|
||||||
|
4. Window thread drains events into a fixed-size ringbuffer
|
||||||
|
5. Renders icicle graph (call hierarchy) and timeline (per-thread activity)
|
||||||
|
|
||||||
|
## Design Goals
|
||||||
|
|
||||||
|
- **Minimal overhead**: Lock-free event recording
|
||||||
|
- **Debug-only**: Compile out in release builds with `#[cfg(debug_assertions)]`
|
||||||
|
- **Separate window**: Doesn't interfere with your app's rendering
|
||||||
|
- **Simple API**: Just `span!("name")` and you're done
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Icicle Graph (Call Stack) │
|
||||||
|
│ ┌──────────────────────────┐ │
|
||||||
|
│ │ frame_work │ │
|
||||||
|
│ ├──────────┬───────────────┤ │
|
||||||
|
│ │ physics │ render │ │
|
||||||
|
│ ├────┬─────┤ │ │
|
||||||
|
│ │ w0 │ w1 │ │ │
|
||||||
|
│ └────┴─────┴───────────────┘ │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ Thread Timeline │
|
||||||
|
│ Main: ████████████████████ │
|
||||||
|
│ Work 0: ░░██████░░░░░░░░░░░ │
|
||||||
|
│ Work 1: ░░░░░░██████░░░░░░░ │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Run the included examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Multi-threaded physics simulation
|
||||||
|
cargo run --example demo
|
||||||
|
|
||||||
|
# Bouncing ball with color-picking thread (30 FPS)
|
||||||
|
cargo run --example bouncing_ball
|
||||||
|
```
|
||||||
|
|
||||||
|
The bouncing ball example demonstrates:
|
||||||
|
- Main thread running at 30 FPS with clear frame gaps
|
||||||
|
- Background thread spawned on wall collision to pick colors
|
||||||
|
- Clear visual separation between thread activities
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT / Apache-2.0 (choose whichever you prefer)
|
||||||
204
examples/bouncing_ball.rs
Normal file
204
examples/bouncing_ball.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
use minifb::{Key, Window, WindowOptions};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
const WIDTH: usize = 800;
|
||||||
|
const HEIGHT: usize = 600;
|
||||||
|
const BALL_RADIUS: usize = 20;
|
||||||
|
|
||||||
|
// Simple ball state
|
||||||
|
struct Ball {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
vx: f32,
|
||||||
|
vy: f32,
|
||||||
|
color: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ball {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
x: 400.0,
|
||||||
|
y: 300.0,
|
||||||
|
vx: 200.0, // pixels per second
|
||||||
|
vy: 150.0,
|
||||||
|
color: 0xFF6464FF, // Red-ish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Start the telemetry window
|
||||||
|
teleprof::start();
|
||||||
|
|
||||||
|
println!("Bouncing Ball Demo");
|
||||||
|
println!("The ball window should appear alongside the profiler");
|
||||||
|
println!("Press Space in profiler window to pause");
|
||||||
|
println!("Press Escape in either window to quit");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut window = Window::new(
|
||||||
|
"Bouncing Ball",
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT,
|
||||||
|
WindowOptions::default(),
|
||||||
|
)
|
||||||
|
.expect("Failed to create window");
|
||||||
|
|
||||||
|
window.set_target_fps(30);
|
||||||
|
|
||||||
|
let ball = Arc::new(Mutex::new(Ball::new()));
|
||||||
|
let mut framebuffer = vec![0u32; WIDTH * HEIGHT];
|
||||||
|
|
||||||
|
// Target 30 FPS
|
||||||
|
let frame_time = Duration::from_millis(33);
|
||||||
|
let mut frame_count = 0;
|
||||||
|
|
||||||
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
|
let frame_start = Instant::now();
|
||||||
|
|
||||||
|
teleprof::span!("main_frame");
|
||||||
|
|
||||||
|
// Check if paused
|
||||||
|
if teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
println!("Paused!");
|
||||||
|
while teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
println!("Resumed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update physics
|
||||||
|
let hit_wall = update_physics(&ball, frame_time.as_secs_f32());
|
||||||
|
|
||||||
|
// If we hit a wall, spawn a thread to pick a new color
|
||||||
|
if hit_wall {
|
||||||
|
let ball_clone = Arc::clone(&ball);
|
||||||
|
thread::spawn(move || {
|
||||||
|
pick_new_color(ball_clone);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render
|
||||||
|
render(&ball, &mut framebuffer);
|
||||||
|
|
||||||
|
// Update window
|
||||||
|
window
|
||||||
|
.update_with_buffer(&framebuffer, WIDTH, HEIGHT)
|
||||||
|
.expect("Failed to update window");
|
||||||
|
|
||||||
|
frame_count += 1;
|
||||||
|
if frame_count % 30 == 0 {
|
||||||
|
print_status(&ball, frame_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep to maintain 30fps
|
||||||
|
let elapsed = frame_start.elapsed();
|
||||||
|
if elapsed < frame_time {
|
||||||
|
thread::sleep(frame_time - elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_physics(ball: &Arc<Mutex<Ball>>, dt: f32) -> bool {
|
||||||
|
teleprof::span!("update_physics");
|
||||||
|
|
||||||
|
let mut ball = ball.lock().unwrap();
|
||||||
|
|
||||||
|
// Update position
|
||||||
|
ball.x += ball.vx * dt;
|
||||||
|
ball.y += ball.vy * dt;
|
||||||
|
|
||||||
|
let mut hit_wall = false;
|
||||||
|
|
||||||
|
// Bounce off walls
|
||||||
|
let radius = BALL_RADIUS as f32;
|
||||||
|
if ball.x - radius < 0.0 || ball.x + radius > WIDTH as f32 {
|
||||||
|
ball.vx = -ball.vx;
|
||||||
|
ball.x = ball.x.clamp(radius, WIDTH as f32 - radius);
|
||||||
|
hit_wall = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ball.y - radius < 0.0 || ball.y + radius > HEIGHT as f32 {
|
||||||
|
ball.vy = -ball.vy;
|
||||||
|
ball.y = ball.y.clamp(radius, HEIGHT as f32 - radius);
|
||||||
|
hit_wall = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate some physics computation
|
||||||
|
thread::sleep(Duration::from_millis(5));
|
||||||
|
|
||||||
|
hit_wall
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pick_new_color(ball: Arc<Mutex<Ball>>) {
|
||||||
|
teleprof::span!("pick_new_color");
|
||||||
|
|
||||||
|
// Simulate some "expensive" color selection
|
||||||
|
thread::sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let r = rng.gen_range(50..255);
|
||||||
|
let g = rng.gen_range(50..255);
|
||||||
|
let b = rng.gen_range(50..255);
|
||||||
|
|
||||||
|
let color = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
|
||||||
|
|
||||||
|
let mut ball = ball.lock().unwrap();
|
||||||
|
ball.color = color;
|
||||||
|
|
||||||
|
println!(" → New color selected: RGB({}, {}, {})", r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(ball: &Arc<Mutex<Ball>>, framebuffer: &mut [u32]) {
|
||||||
|
teleprof::span!("render");
|
||||||
|
|
||||||
|
{
|
||||||
|
teleprof::span!("clear_background");
|
||||||
|
// Clear to dark gray
|
||||||
|
framebuffer.fill(0x2A2A2AFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
teleprof::span!("draw_ball");
|
||||||
|
let ball = ball.lock().unwrap();
|
||||||
|
|
||||||
|
// Draw ball as a filled circle
|
||||||
|
let cx = ball.x as i32;
|
||||||
|
let cy = ball.y as i32;
|
||||||
|
let radius = BALL_RADIUS as i32;
|
||||||
|
|
||||||
|
for dy in -radius..=radius {
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
// Check if point is inside circle
|
||||||
|
if dx * dx + dy * dy <= radius * radius {
|
||||||
|
let x = cx + dx;
|
||||||
|
let y = cy + dy;
|
||||||
|
|
||||||
|
if x >= 0 && x < WIDTH as i32 && y >= 0 && y < HEIGHT as i32 {
|
||||||
|
let idx = y as usize * WIDTH + x as usize;
|
||||||
|
framebuffer[idx] = ball.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
teleprof::span!("submit_frame");
|
||||||
|
// Simulate GPU submission
|
||||||
|
thread::sleep(Duration::from_millis(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_status(ball: &Arc<Mutex<Ball>>, frame: u32) {
|
||||||
|
teleprof::span!("print_status");
|
||||||
|
|
||||||
|
let ball = ball.lock().unwrap();
|
||||||
|
println!(
|
||||||
|
"Frame {}: Ball at ({:.1}, {:.1})",
|
||||||
|
frame, ball.x, ball.y
|
||||||
|
);
|
||||||
|
}
|
||||||
83
examples/demo.rs
Normal file
83
examples/demo.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Start the telemetry window
|
||||||
|
teleprof::start();
|
||||||
|
|
||||||
|
println!("Teleprof demo running...");
|
||||||
|
println!("Press Space in the profiler window to pause/unpause");
|
||||||
|
println!("Press Escape in the profiler window to quit");
|
||||||
|
|
||||||
|
// Simulate some work
|
||||||
|
for i in 0..1000 {
|
||||||
|
frame_work(i);
|
||||||
|
|
||||||
|
// Check if paused
|
||||||
|
if teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
println!("Paused!");
|
||||||
|
while teleprof::PAUSE.try_lock().is_err() {
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
println!("Resumed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame_work(frame: u32) {
|
||||||
|
teleprof::span!("frame_work");
|
||||||
|
|
||||||
|
physics_update();
|
||||||
|
render();
|
||||||
|
|
||||||
|
if frame % 10 == 0 {
|
||||||
|
occasional_task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn physics_update() {
|
||||||
|
teleprof::span!("physics_update");
|
||||||
|
|
||||||
|
// Spawn some worker threads
|
||||||
|
let handles: Vec<_> = (0..3).map(|i| {
|
||||||
|
thread::spawn(move || {
|
||||||
|
physics_worker(i);
|
||||||
|
})
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn physics_worker(id: u32) {
|
||||||
|
teleprof::span!("physics_worker");
|
||||||
|
|
||||||
|
// Simulate work
|
||||||
|
let work_ms = 5 + (id * 2);
|
||||||
|
thread::sleep(Duration::from_millis(work_ms as u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render() {
|
||||||
|
teleprof::span!("render");
|
||||||
|
|
||||||
|
build_command_buffer();
|
||||||
|
submit_to_gpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_command_buffer() {
|
||||||
|
teleprof::span!("build_command_buffer");
|
||||||
|
thread::sleep(Duration::from_millis(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_to_gpu() {
|
||||||
|
teleprof::span!("submit_to_gpu");
|
||||||
|
thread::sleep(Duration::from_millis(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn occasional_task() {
|
||||||
|
teleprof::span!("occasional_task");
|
||||||
|
thread::sleep(Duration::from_millis(10));
|
||||||
|
}
|
||||||
28
shell.nix
Normal file
28
shell.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
with import <nixpkgs> { };
|
||||||
|
|
||||||
|
let
|
||||||
|
buildInputs = [
|
||||||
|
# For minifb
|
||||||
|
xorg.libX11
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXrandr
|
||||||
|
xorg.libXi
|
||||||
|
|
||||||
|
# Wayland support
|
||||||
|
wayland
|
||||||
|
libxkbcommon
|
||||||
|
];
|
||||||
|
in
|
||||||
|
mkShell {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
|
||||||
|
inherit buildInputs;
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
export LD_LIBRARY_PATH="${lib.makeLibraryPath buildInputs}:$LD_LIBRARY_PATH"
|
||||||
|
echo "Teleprof dev environment loaded"
|
||||||
|
echo "Run: cargo run --example demo"
|
||||||
|
'';
|
||||||
|
}
|
||||||
458
src/lib.rs
Normal file
458
src/lib.rs
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Public API
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Global PAUSE lock - acquire this to pause the application
|
||||||
|
pub static PAUSE: Lazy<Arc<Mutex<()>>> = Lazy::new(|| Arc::new(Mutex::new(())));
|
||||||
|
|
||||||
|
/// Start the telemetry window in a separate thread
|
||||||
|
pub fn start() {
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
if let Err(e) = window::run() {
|
||||||
|
eprintln!("Teleprof window error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a profiling span - use via the `span!` macro
|
||||||
|
pub struct SpanGuard {
|
||||||
|
span_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpanGuard {
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
let span_id = next_span_id();
|
||||||
|
let thread_id = std::thread::current().id();
|
||||||
|
let parent_id = PARENT_SPAN.with(|p| p.get());
|
||||||
|
|
||||||
|
// Set ourselves as the current parent for nested spans
|
||||||
|
PARENT_SPAN.with(|p| p.set(Some(span_id)));
|
||||||
|
|
||||||
|
EVENT_SENDER.send(Event::SpanStart {
|
||||||
|
span_id,
|
||||||
|
parent_id,
|
||||||
|
thread_id: thread_id_to_u64(thread_id),
|
||||||
|
name,
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
}).ok();
|
||||||
|
|
||||||
|
Self { span_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SpanGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
EVENT_SENDER.send(Event::SpanEnd {
|
||||||
|
span_id: self.span_id,
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
}).ok();
|
||||||
|
|
||||||
|
// Pop back to parent
|
||||||
|
PARENT_SPAN.with(|p| {
|
||||||
|
// Find parent by looking at active spans (simplified - just clear for now)
|
||||||
|
p.set(None);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro for creating a span
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! span {
|
||||||
|
($name:expr) => {
|
||||||
|
let _span_guard = $crate::SpanGuard::new($name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Internal types and state
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum Event {
|
||||||
|
SpanStart {
|
||||||
|
span_id: u64,
|
||||||
|
parent_id: Option<u64>,
|
||||||
|
thread_id: u64,
|
||||||
|
name: &'static str,
|
||||||
|
timestamp: Instant,
|
||||||
|
},
|
||||||
|
SpanEnd {
|
||||||
|
span_id: u64,
|
||||||
|
timestamp: Instant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static PARENT_SPAN: Cell<Option<u64>> = Cell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
static EVENT_SENDER: Lazy<Sender<Event>> = Lazy::new(|| {
|
||||||
|
let (tx, rx) = unbounded();
|
||||||
|
*EVENT_RECEIVER.lock().unwrap() = Some(rx);
|
||||||
|
tx
|
||||||
|
});
|
||||||
|
|
||||||
|
static EVENT_RECEIVER: Lazy<Arc<Mutex<Option<Receiver<Event>>>>> =
|
||||||
|
Lazy::new(|| Arc::new(Mutex::new(None)));
|
||||||
|
|
||||||
|
static SPAN_ID_COUNTER: Lazy<Arc<Mutex<u64>>> = Lazy::new(|| Arc::new(Mutex::new(0)));
|
||||||
|
|
||||||
|
fn next_span_id() -> u64 {
|
||||||
|
let mut counter = SPAN_ID_COUNTER.lock().unwrap();
|
||||||
|
let id = *counter;
|
||||||
|
*counter += 1;
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thread_id_to_u64(id: std::thread::ThreadId) -> u64 {
|
||||||
|
// Hack: ThreadId doesn't expose inner value, so we format and parse
|
||||||
|
let s = format!("{:?}", id);
|
||||||
|
s.trim_start_matches("ThreadId(")
|
||||||
|
.trim_end_matches(")")
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Window rendering
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
mod window {
|
||||||
|
use super::*;
|
||||||
|
use minifb::{Key, Window, WindowOptions};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const INITIAL_WIDTH: usize = 1280;
|
||||||
|
const INITIAL_HEIGHT: usize = 720;
|
||||||
|
const MAX_EVENTS: usize = 1_000_000; // ~16MB at 16 bytes per event
|
||||||
|
|
||||||
|
// Monokai palette
|
||||||
|
const COLORS: [u32; 8] = [
|
||||||
|
0xF92672, // Pink
|
||||||
|
0xA6E22E, // Green
|
||||||
|
0xFD971F, // Orange
|
||||||
|
0x66D9EF, // Cyan
|
||||||
|
0xAE81FF, // Purple
|
||||||
|
0xE6DB74, // Yellow
|
||||||
|
0xF8F8F2, // White
|
||||||
|
0x75715E, // Gray
|
||||||
|
];
|
||||||
|
|
||||||
|
const BG_COLOR: u32 = 0x272822;
|
||||||
|
const GRID_COLOR: u32 = 0x3E3D32;
|
||||||
|
|
||||||
|
struct CompletedSpan {
|
||||||
|
span_id: u64,
|
||||||
|
parent_id: Option<u64>,
|
||||||
|
thread_id: u64,
|
||||||
|
name: &'static str,
|
||||||
|
start: Instant,
|
||||||
|
end: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RingBuffer {
|
||||||
|
spans: Vec<CompletedSpan>,
|
||||||
|
head: usize,
|
||||||
|
pending_starts: HashMap<u64, (u64, Option<u64>, u64, &'static str, Instant)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RingBuffer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
spans: Vec::with_capacity(MAX_EVENTS),
|
||||||
|
head: 0,
|
||||||
|
pending_starts: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_event(&mut self, event: Event) {
|
||||||
|
match event {
|
||||||
|
Event::SpanStart { span_id, parent_id, thread_id, name, timestamp } => {
|
||||||
|
self.pending_starts.insert(span_id, (span_id, parent_id, thread_id, name, timestamp));
|
||||||
|
}
|
||||||
|
Event::SpanEnd { span_id, timestamp } => {
|
||||||
|
if let Some((span_id, parent_id, thread_id, name, start)) = self.pending_starts.remove(&span_id) {
|
||||||
|
let span = CompletedSpan {
|
||||||
|
span_id,
|
||||||
|
parent_id,
|
||||||
|
thread_id,
|
||||||
|
name,
|
||||||
|
start,
|
||||||
|
end: timestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.spans.len() < MAX_EVENTS {
|
||||||
|
self.spans.push(span);
|
||||||
|
} else {
|
||||||
|
self.spans[self.head] = span;
|
||||||
|
self.head = (self.head + 1) % MAX_EVENTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter(&self) -> Box<dyn Iterator<Item = &CompletedSpan> + '_> {
|
||||||
|
if self.spans.len() < MAX_EVENTS {
|
||||||
|
Box::new(self.spans.iter())
|
||||||
|
} else {
|
||||||
|
// Return items in chronological order from ringbuffer
|
||||||
|
Box::new(self.spans[self.head..].iter().chain(self.spans[..self.head].iter()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ViewState {
|
||||||
|
time_offset: f64, // seconds
|
||||||
|
time_scale: f64, // pixels per second
|
||||||
|
paused: bool,
|
||||||
|
pause_guard: Option<std::sync::MutexGuard<'static, ()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut window = Window::new(
|
||||||
|
"Teleprof",
|
||||||
|
INITIAL_WIDTH,
|
||||||
|
INITIAL_HEIGHT,
|
||||||
|
WindowOptions::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
window.set_target_fps(60);
|
||||||
|
|
||||||
|
let receiver = EVENT_RECEIVER.lock().unwrap().take()
|
||||||
|
.ok_or("Event receiver not initialized")?;
|
||||||
|
|
||||||
|
let mut buffer = RingBuffer::new();
|
||||||
|
let mut view = ViewState {
|
||||||
|
time_offset: 0.0,
|
||||||
|
time_scale: 100.0, // 100 pixels per second
|
||||||
|
paused: false,
|
||||||
|
pause_guard: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut framebuffer = vec![BG_COLOR; INITIAL_WIDTH * INITIAL_HEIGHT];
|
||||||
|
|
||||||
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||||
|
// Drain events from channel
|
||||||
|
while let Ok(event) = receiver.try_recv() {
|
||||||
|
buffer.push_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle input
|
||||||
|
if window.is_key_pressed(Key::Space, minifb::KeyRepeat::No) {
|
||||||
|
view.paused = !view.paused;
|
||||||
|
if view.paused {
|
||||||
|
view.pause_guard = PAUSE.try_lock().ok();
|
||||||
|
} else {
|
||||||
|
view.pause_guard = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current window size
|
||||||
|
let (width, height) = window.get_size();
|
||||||
|
if framebuffer.len() != width * height {
|
||||||
|
framebuffer.resize(width * height, BG_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear framebuffer
|
||||||
|
framebuffer.fill(BG_COLOR);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
render_frame(&mut framebuffer, width, height, &buffer, &view);
|
||||||
|
|
||||||
|
window.update_with_buffer(&framebuffer, width, height)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_frame(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
buffer: &RingBuffer,
|
||||||
|
view: &ViewState,
|
||||||
|
) {
|
||||||
|
let icicle_height = height / 2;
|
||||||
|
let timeline_height = height - icicle_height;
|
||||||
|
|
||||||
|
// Find time range
|
||||||
|
let spans: Vec<_> = buffer.iter().collect();
|
||||||
|
if spans.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let earliest = spans.iter().map(|s| s.start).min().unwrap();
|
||||||
|
let latest = spans.iter().map(|s| s.end).max().unwrap();
|
||||||
|
let duration = (latest - earliest).as_secs_f64();
|
||||||
|
|
||||||
|
// Draw icicle graph (top half)
|
||||||
|
render_icicle(framebuffer, width, icicle_height, &spans, earliest, duration, view);
|
||||||
|
|
||||||
|
// Draw timeline (bottom half)
|
||||||
|
render_timeline(
|
||||||
|
framebuffer,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
icicle_height,
|
||||||
|
timeline_height,
|
||||||
|
&spans,
|
||||||
|
earliest,
|
||||||
|
duration,
|
||||||
|
view,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_icicle(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
spans: &[&CompletedSpan],
|
||||||
|
earliest: Instant,
|
||||||
|
duration: f64,
|
||||||
|
view: &ViewState,
|
||||||
|
) {
|
||||||
|
// Build tree structure
|
||||||
|
let mut roots = Vec::new();
|
||||||
|
let mut children: HashMap<u64, Vec<&CompletedSpan>> = HashMap::new();
|
||||||
|
|
||||||
|
for span in spans {
|
||||||
|
if let Some(parent) = span.parent_id {
|
||||||
|
children.entry(parent).or_default().push(span);
|
||||||
|
} else {
|
||||||
|
roots.push(*span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render each root and its children recursively
|
||||||
|
let y_start = 0;
|
||||||
|
let row_height = 20;
|
||||||
|
|
||||||
|
for root in roots {
|
||||||
|
render_icicle_span(
|
||||||
|
framebuffer,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
root,
|
||||||
|
&children,
|
||||||
|
earliest,
|
||||||
|
duration,
|
||||||
|
y_start,
|
||||||
|
row_height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_icicle_span(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
span: &CompletedSpan,
|
||||||
|
children: &HashMap<u64, Vec<&CompletedSpan>>,
|
||||||
|
earliest: Instant,
|
||||||
|
duration: f64,
|
||||||
|
y: usize,
|
||||||
|
row_height: usize,
|
||||||
|
) {
|
||||||
|
if y + row_height > height {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time = (span.start - earliest).as_secs_f64();
|
||||||
|
let end_time = (span.end - earliest).as_secs_f64();
|
||||||
|
|
||||||
|
let x1 = ((start_time / duration) * width as f64) as usize;
|
||||||
|
let x2 = ((end_time / duration) * width as f64) as usize;
|
||||||
|
|
||||||
|
let color = get_color_for_name(span.name);
|
||||||
|
|
||||||
|
fill_rect(framebuffer, width, x1, y, x2 - x1, row_height, color);
|
||||||
|
|
||||||
|
// Render children
|
||||||
|
if let Some(child_spans) = children.get(&span.span_id) {
|
||||||
|
let child_y = y + row_height;
|
||||||
|
for child in child_spans {
|
||||||
|
render_icicle_span(
|
||||||
|
framebuffer,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
child,
|
||||||
|
children,
|
||||||
|
earliest,
|
||||||
|
duration,
|
||||||
|
child_y,
|
||||||
|
row_height,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_timeline(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
_height: usize,
|
||||||
|
y_offset: usize,
|
||||||
|
timeline_height: usize,
|
||||||
|
spans: &[&CompletedSpan],
|
||||||
|
earliest: Instant,
|
||||||
|
duration: f64,
|
||||||
|
_view: &ViewState,
|
||||||
|
) {
|
||||||
|
// Group by thread
|
||||||
|
let mut threads: HashMap<u64, Vec<&CompletedSpan>> = HashMap::new();
|
||||||
|
for span in spans {
|
||||||
|
threads.entry(span.thread_id).or_default().push(*span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let thread_ids: Vec<_> = threads.keys().copied().collect();
|
||||||
|
let num_threads = thread_ids.len();
|
||||||
|
if num_threads == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let row_height = timeline_height / num_threads.max(1);
|
||||||
|
|
||||||
|
for (i, thread_id) in thread_ids.iter().enumerate() {
|
||||||
|
let y = y_offset + i * row_height;
|
||||||
|
let thread_spans = &threads[thread_id];
|
||||||
|
|
||||||
|
for span in thread_spans {
|
||||||
|
let start_time = (span.start - earliest).as_secs_f64();
|
||||||
|
let end_time = (span.end - earliest).as_secs_f64();
|
||||||
|
|
||||||
|
let x1 = ((start_time / duration) * width as f64) as usize;
|
||||||
|
let x2 = ((end_time / duration) * width as f64).max(x1 as f64 + 1.0) as usize;
|
||||||
|
|
||||||
|
let color = get_color_for_name(span.name);
|
||||||
|
|
||||||
|
fill_rect(framebuffer, width, x1, y, x2 - x1, row_height - 2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_rect(framebuffer: &mut [u32], width: usize, x: usize, y: usize, w: usize, h: usize, color: u32) {
|
||||||
|
for dy in 0..h {
|
||||||
|
let row = y + dy;
|
||||||
|
for dx in 0..w {
|
||||||
|
let col = x + dx;
|
||||||
|
if col < width {
|
||||||
|
let idx = row * width + col;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
framebuffer[idx] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color_for_name(name: &str) -> u32 {
|
||||||
|
let hash = name.bytes().fold(0u32, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u32));
|
||||||
|
COLORS[(hash as usize) % COLORS.len()]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user