From aa7b0f6eed3b5cd4d9ce7dbbfb0c4bcf542ef3e2 Mon Sep 17 00:00:00 2001 From: funman300 Date: Sun, 17 May 2026 20:37:01 -0700 Subject: [PATCH] perf(engine): gate frame-hot ECS systems on resource changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - find_draggable_at: break instead of return None on non-top non-tableau hit so remaining pile searches are not abandoned early (M-9) - update_stock_count_badge: run only when GameStateResource changes (M-5) - update_drop_highlights: run only when DragState changes (M-6) - update_high_contrast_borders/backgrounds: run only when SettingsResource changes (M-7) - update_selection_hud: run only when SelectionState or GameStateResource changes; uses resource_exists_and_changed to avoid panic in tests where SelectionState is not registered (M-8) - Volume toast threshold: f32::EPSILON → 0.001 to avoid spurious toasts from float rounding noise in settings events (M-10) - check_no_moves: collapse read().next().is_some() + clear() into a single read().count() > 0 drain (M-11) Co-Authored-By: Claude Sonnet 4.6 --- solitaire_engine/src/animation_plugin.rs | 4 ++-- solitaire_engine/src/card_plugin.rs | 4 +++- solitaire_engine/src/cursor_plugin.rs | 2 +- solitaire_engine/src/game_plugin.rs | 4 +--- solitaire_engine/src/hud_plugin.rs | 8 +++++++- solitaire_engine/src/input_plugin.rs | 4 +++- solitaire_engine/src/settings_plugin.rs | 6 ++++-- 7 files changed, 21 insertions(+), 11 deletions(-) diff --git a/solitaire_engine/src/animation_plugin.rs b/solitaire_engine/src/animation_plugin.rs index f4271c3..ecf93ee 100644 --- a/solitaire_engine/src/animation_plugin.rs +++ b/solitaire_engine/src/animation_plugin.rs @@ -454,8 +454,8 @@ fn handle_settings_toast( for ev in events.read() { let sfx = ev.0.sfx_volume; let music = ev.0.music_volume; - let sfx_changed = last_sfx.is_none_or(|prev| (prev - sfx).abs() > f32::EPSILON); - let music_changed = last_music.is_none_or(|prev| (prev - music).abs() > f32::EPSILON); + let sfx_changed = last_sfx.is_none_or(|prev| (prev - sfx).abs() > 0.001); + let music_changed = last_music.is_none_or(|prev| (prev - music).abs() > 0.001); *last_sfx = Some(sfx); *last_music = Some(music); if sfx_changed { diff --git a/solitaire_engine/src/card_plugin.rs b/solitaire_engine/src/card_plugin.rs index 30ba696..1b1c53d 100644 --- a/solitaire_engine/src/card_plugin.rs +++ b/solitaire_engine/src/card_plugin.rs @@ -451,7 +451,9 @@ impl Plugin for CardPlugin { clear_right_click_highlights_on_state_change.after(GameMutation), clear_right_click_highlights_on_pause, update_stock_empty_indicator.after(GameMutation), - update_stock_count_badge.after(GameMutation), + update_stock_count_badge + .after(GameMutation) + .run_if(resource_changed::), collect_resize_events.after(LayoutSystem::UpdateOnResize), snap_cards_on_window_resize.after(collect_resize_events), ), diff --git a/solitaire_engine/src/cursor_plugin.rs b/solitaire_engine/src/cursor_plugin.rs index d0e871c..5531528 100644 --- a/solitaire_engine/src/cursor_plugin.rs +++ b/solitaire_engine/src/cursor_plugin.rs @@ -80,7 +80,7 @@ impl Plugin for CursorPlugin { Update, ( update_cursor_icon, - update_drop_highlights, + update_drop_highlights.run_if(resource_changed::), update_drop_target_overlays, ), ); diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index 77ebb2a..3da6afe 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -1079,9 +1079,7 @@ fn check_no_moves( ) { // Reset the debounce flag on every state change so if something changes // we re-evaluate on the next state change. - let had_event = events.read().next().is_some(); - // Drain remaining events to avoid leaking. - events.clear(); + let had_event = events.read().count() > 0; if !had_event { return; diff --git a/solitaire_engine/src/hud_plugin.rs b/solitaire_engine/src/hud_plugin.rs index b861fd1..e255c23 100644 --- a/solitaire_engine/src/hud_plugin.rs +++ b/solitaire_engine/src/hud_plugin.rs @@ -417,7 +417,13 @@ impl Plugin for HudPlugin { .add_systems(Update, (update_hud_avatar, handle_avatar_button)) .add_systems(Update, update_won_previously.after(GameMutation)) .add_systems(Update, announce_auto_complete.after(GameMutation)) - .add_systems(Update, update_selection_hud) + .add_systems( + Update, + update_selection_hud.run_if( + resource_exists_and_changed:: + .or(resource_exists_and_changed::), + ), + ) .add_systems(Update, update_hud_typography) .add_systems( Update, diff --git a/solitaire_engine/src/input_plugin.rs b/solitaire_engine/src/input_plugin.rs index 0c82d2d..159c3e8 100644 --- a/solitaire_engine/src/input_plugin.rs +++ b/solitaire_engine/src/input_plugin.rs @@ -1162,7 +1162,9 @@ fn find_draggable_at( (i, pile_cards.cards.len()) } else { if i != pile_cards.cards.len() - 1 { - return None; + // Non-top card on a non-tableau pile — not draggable; skip + // this pile and continue searching remaining piles. + break; } (i, i + 1) }; diff --git a/solitaire_engine/src/settings_plugin.rs b/solitaire_engine/src/settings_plugin.rs index 0b4cdce..aeea65d 100644 --- a/solitaire_engine/src/settings_plugin.rs +++ b/solitaire_engine/src/settings_plugin.rs @@ -401,8 +401,10 @@ impl Plugin for SettingsPlugin { update_anim_speed_text, update_color_blind_text, update_high_contrast_text, - update_high_contrast_borders, - update_high_contrast_backgrounds, + update_high_contrast_borders + .run_if(resource_changed::), + update_high_contrast_backgrounds + .run_if(resource_changed::), update_reduce_motion_text, update_tooltip_delay_text, update_time_bonus_multiplier_text,