reaction 0.2.0

Universal low-latency input handling for game engines
Documentation
use std::time::Instant;

pub struct AdaptiveFramePacer {
    target_fps: u32,
    frame_history: Vec<f64>,       // Frame times in ms
    _prediction_error_margin: f64, // ms
    last_frame_start: Instant,
}

impl AdaptiveFramePacer {
    pub fn new(target_fps: u32) -> Self {
        Self {
            target_fps,
            frame_history: Vec::with_capacity(60),
            _prediction_error_margin: 0.5, // 0.5ms safety
            last_frame_start: Instant::now(),
        }
    }

    pub fn target_frame_time_ms(&self) -> f64 {
        1000.0 / self.target_fps as f64
    }

    pub fn mark_frame_start(&mut self) {
        self.last_frame_start = Instant::now();
    }

    pub fn mark_frame_end(&mut self) {
        let duration = self.last_frame_start.elapsed().as_secs_f64() * 1000.0;
        self.frame_history.push(duration);
        if self.frame_history.len() > 60 {
            self.frame_history.remove(0);
        }
    }

    fn average_frame_time(&self) -> f64 {
        if self.frame_history.is_empty() {
            return self.target_frame_time_ms();
        }
        self.frame_history.iter().sum::<f64>() / self.frame_history.len() as f64
    }

    /// Returns time to wait/sleep before sampling input to align closely with VSync/Render
    /// Returns 0.0 if we should sample immediately.
    pub fn time_until_input_sample(&self, _present_time_estimate: Instant) -> f64 {
        // Simple strategy: We want to sample input as late as possible,
        // effectively (PresentTime - EstimatedFrameProcessingTime - SafetyMargin).
        // For MVP, assume we call this at the start of the frame loop?
        // Or users loop calls this?

        // Frame structure:
        // Start -> Sleep(time_until_input) -> Poll Input -> Update -> Render -> Present

        // We know Average Update+Render time from history.
        let _avg_work_time = self.average_frame_time();

        // But `mark_frame_end` usually measures TOTAL frame time including Wait?
        // We need to measure WORK time separately.
        // For now, let's assume `mark_frame_end` is called before SwapBuffers/Wait.

        // Stub: Just return 0 for now until integrated with a real loop structure
        0.0
    }
}