From fc752c950f6f2273290c74e7fb1f557eb2e57b08 Mon Sep 17 00:00:00 2001 From: Eval Exec Date: Thu, 8 Oct 2026 02:06:04 +0800 Subject: [PATCH 1/2] refactor(runtime): use enumset for frame demand reasons --- Cargo.lock | 64 +++++++++++++++++-- Cargo.toml | 1 + crates/neomacs-display-runtime/Cargo.toml | 1 + .../src/render_thread/frame_sched.rs | 42 ++---------- .../render_thread/tests/frame_sched_test.rs | 17 +++++ 5 files changed, 83 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7efec9f895..d86430f4bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,7 +657,7 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4654961ad0494e4774c5c60b4cb4cd0ae9b9d92d039d901638b1dba97ebebf5" dependencies = [ - "darling", + "darling 0.24.1", "ident_case", "prettyplease 0.3.0", "proc-macro2", @@ -1482,14 +1482,37 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + [[package]] name = "darling" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.24.1", + "darling_macro 0.24.1", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -1505,13 +1528,24 @@ dependencies = [ "syn 3.0.4", ] +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.119", +] + [[package]] name = "darling_macro" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ - "darling_core", + "darling_core 0.24.1", "quote", "syn 3.0.4", ] @@ -1757,6 +1791,27 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "enumset" +version = "1.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc5801fd11762e24d1e420d01d2ac518f2a2ca4329d4fbb6639f2412b6204e0" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd536557b58c682b217b8fb199afdff47cd3eff260623f19e77074eb073d63a" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "equator" version = "0.4.2" @@ -3834,6 +3889,7 @@ dependencies = [ "cosmic-text", "crossbeam-channel", "crossterm", + "enumset", "fontdb", "gio", "image", diff --git a/Cargo.toml b/Cargo.toml index 4462d67351..3efcd154a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -174,6 +174,7 @@ rustc-hash = "2" hashbrown = "0.17" num_enum = "0.7.6" enum_dispatch = "0.3.13" +enumset = "1.1.14" strum = { version = "0.28", features = ["derive"] } unicode_names2 = "3.1.0" unicode-script = "0.5" diff --git a/crates/neomacs-display-runtime/Cargo.toml b/crates/neomacs-display-runtime/Cargo.toml index c36f406ccd..9d5d89a618 100644 --- a/crates/neomacs-display-runtime/Cargo.toml +++ b/crates/neomacs-display-runtime/Cargo.toml @@ -37,6 +37,7 @@ tracing.workspace = true tracing-subscriber.workspace = true serde_json.workspace = true strum.workspace = true +enumset.workspace = true num_enum.workspace = true thiserror.workspace = true bitflags.workspace = true diff --git a/crates/neomacs-display-runtime/src/render_thread/frame_sched.rs b/crates/neomacs-display-runtime/src/render_thread/frame_sched.rs index d443974730..4f05267666 100644 --- a/crates/neomacs-display-runtime/src/render_thread/frame_sched.rs +++ b/crates/neomacs-display-runtime/src/render_thread/frame_sched.rs @@ -135,7 +135,8 @@ macro_rules! demand_reasons { // Interface variants/fields defined by the scheduling plan; consumed as // later stages migrate effects onto the coordinator. #[allow(dead_code)] - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, enumset::EnumSetType)] + #[enumset(no_super_impls, no_ops)] pub(crate) enum DemandReason { $($(#[$variant_meta])* $variant,)+ } @@ -221,43 +222,8 @@ impl DemandReason { /// Set of [`DemandReason`]s, carried by value on a [`FramePlan`] so a frame can /// be attributed to what asked for it ("why did this present happen?"). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub(crate) struct DemandReasonSet(u32); - -impl DemandReasonSet { - pub(crate) const fn empty() -> Self { - DemandReasonSet(0) - } - - fn insert(&mut self, reason: DemandReason) { - self.0 |= 1 << reason.index(); - } - - pub(crate) fn contains(self, reason: DemandReason) -> bool { - self.0 & (1 << reason.index()) != 0 - } - - pub(crate) fn is_empty(self) -> bool { - self.0 == 0 - } - - /// Reasons in [`DemandReason::ALL`] order. - pub(crate) fn iter(self) -> impl Iterator { - DemandReason::ALL - .into_iter() - .filter(move |r| self.contains(*r)) - } -} - -impl FromIterator for DemandReasonSet { - fn from_iter>(iter: I) -> Self { - let mut set = DemandReasonSet::empty(); - for reason in iter { - set.insert(reason); - } - set - } -} +/// Iteration follows declaration order, matching [`DemandReason::ALL`]. +pub(crate) type DemandReasonSet = enumset::EnumSet; /// A declaration that pixels need to change, with reason, scope, and cadence. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs b/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs index 8ec3437eb5..bf23d76f9b 100644 --- a/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs +++ b/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs @@ -1253,3 +1253,20 @@ fn servicing_twice_at_one_timestamp_is_idempotent() { "nothing ripe survives one service" ); } + +#[test] +fn demand_reason_sets_deduplicate_and_iterate_in_declaration_order() { + let reasons: DemandReasonSet = DemandReason::ALL + .into_iter() + .rev() + .chain(DemandReason::ALL) + .collect(); + assert_eq!(reasons.iter().collect::>(), DemandReason::ALL); + for reason in DemandReason::ALL { + assert!(reasons.contains(reason), "missing {reason:?}"); + } + let empty = DemandReasonSet::default(); + assert!(empty.is_empty()); + assert!(empty.iter().next().is_none()); + assert!(!empty.contains(DemandReason::EditorCommit)); +} From df9b7d668d6035c3149cf1ea4a6c4060a4b13489 Mon Sep 17 00:00:00 2001 From: Eval Exec Date: Thu, 8 Oct 2026 09:54:32 +0800 Subject: [PATCH 2/2] test(runtime): compare demand reason iteration without allocation --- .../src/render_thread/tests/frame_sched_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs b/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs index bf23d76f9b..705902ebf4 100644 --- a/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs +++ b/crates/neomacs-display-runtime/src/render_thread/tests/frame_sched_test.rs @@ -1261,7 +1261,7 @@ fn demand_reason_sets_deduplicate_and_iterate_in_declaration_order() { .rev() .chain(DemandReason::ALL) .collect(); - assert_eq!(reasons.iter().collect::>(), DemandReason::ALL); + assert!(reasons.iter().eq(DemandReason::ALL)); for reason in DemandReason::ALL { assert!(reasons.contains(reason), "missing {reason:?}"); }