Skip to content

[Feature] Add configurable event loop control flow policy to reduce CPU usage when idle #98

@vmarcella

Description

@vmarcella

Overview

The event loop is currently forced into ControlFlow::Poll, keeping the CPU hot even when the application is not actively rendering or animating. This issue proposes adding a configurable control flow policy that allows applications to choose between continuous polling (for games/animations) and wait-based modes (for tools/editors) to reduce CPU usage when idle.

Current State

The event loop unconditionally uses polling mode:

// crates/lambda-rs-platform/src/winit/mod.rs:230-238
pub fn run_forever<Callback>(self, mut callback: Callback)
where
  Callback: 'static + FnMut(Event<E>, &EventLoopWindowTarget<E>),
{
  self
    .event_loop
    .run(move |event, target| {
      target.set_control_flow(ControlFlow::Poll);  // Always polling
      callback(event, target);
    })
}

This means that even when an application is idle (e.g., waiting for user input in a tool or editor), the CPU remains at high utilization spinning through empty event loop iterations.

Scope

Goals:

  • Provide configurable control flow policies: Poll, Wait, and WaitUntil(duration)
  • Allow applications to signal whether they require continuous rendering (animating) or can wait for events
  • Reduce CPU and power consumption for applications that do not need continuous updates

Non-Goals:

  • Automatic detection of animation state (applications must signal intent)
  • Frame pacing or advanced vsync coordination (separate concern)
  • Changing the default behavior for existing applications without opt-in

Proposed API

// crates/lambda-rs-platform/src/winit/mod.rs or appropriate location

/// Control flow policy for the event loop
pub enum EventLoopPolicy {
  /// Continuous polling for games and real-time applications
  Poll,
  /// Wait for events, ideal for tools and editors
  Wait,
  /// Wait until next frame time for fixed-rate rendering
  WaitUntil { target_fps: u32 },
}

// In runtime/application builder:
impl RuntimeBuilder {
  /// Set the event loop control flow policy.
  /// 
  /// - `Poll`: Continuous updates, high CPU usage, lowest latency
  /// - `Wait`: Sleep until events arrive, minimal CPU usage
  /// - `WaitUntil`: Target a specific frame rate
  pub fn with_event_loop_policy(self, policy: EventLoopPolicy) -> Self;
}

// Runtime can expose a method to change policy dynamically:
impl Runtime {
  /// Signal whether the application is currently animating.
  /// When true, uses Poll mode; when false, uses Wait mode.
  pub fn set_animating(&mut self, is_animating: bool);
}

Example Usage:

// For a game (continuous rendering)
let runtime = RuntimeBuilder::new()
  .with_event_loop_policy(EventLoopPolicy::Poll)
  .build()?;

// For an editor (event-driven)
let runtime = RuntimeBuilder::new()
  .with_event_loop_policy(EventLoopPolicy::Wait)
  .build()?;

// For fixed 60 FPS rendering
let runtime = RuntimeBuilder::new()
  .with_event_loop_policy(EventLoopPolicy::WaitUntil { target_fps: 60 })
  .build()?;

Acceptance Criteria

  • EventLoopPolicy enum defined with Poll, Wait, and WaitUntil variants
  • Builder method with_event_loop_policy added to runtime/application builder
  • Event loop respects the configured policy at runtime
  • Optional: Runtime method to dynamically switch between polling and waiting
  • Documentation updated with guidance on when to use each policy
  • Example demonstrating policy configuration added or existing example updated

Affected Crates

lambda-rs-platform, lambda-rs

Notes

  • Consider defaulting to Poll for backward compatibility, or WaitUntil { target_fps: 60 } for better defaults
  • The "is_animating" knob could be a simpler initial implementation before full policy support
  • This is particularly important for laptop/mobile users where CPU usage affects battery life

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions