ringkernel_procint/actors/
runtime.rs1use std::time::{Duration, Instant};
6
7#[derive(Debug, Clone)]
9pub struct RuntimeConfig {
10 pub target_fps: u32,
12 pub max_batch_size: usize,
14 pub use_gpu: bool,
16 pub device_id: u32,
18}
19
20impl Default for RuntimeConfig {
21 fn default() -> Self {
22 Self {
23 target_fps: 60,
24 max_batch_size: 4096,
25 use_gpu: true,
26 device_id: 0,
27 }
28 }
29}
30
31#[derive(Debug)]
33pub struct GpuActorRuntime {
34 config: RuntimeConfig,
36 initialized: bool,
38 device_info: Option<DeviceInfo>,
40 frame_timer: FrameTimer,
42}
43
44impl Default for GpuActorRuntime {
45 fn default() -> Self {
46 Self::new(RuntimeConfig::default())
47 }
48}
49
50impl GpuActorRuntime {
51 pub fn new(config: RuntimeConfig) -> Self {
53 Self {
54 config,
55 initialized: false,
56 device_info: None,
57 frame_timer: FrameTimer::new(60),
58 }
59 }
60
61 pub fn init(&mut self) -> Result<(), String> {
63 if self.initialized {
64 return Ok(());
65 }
66
67 if self.config.use_gpu {
69 self.device_info = Self::detect_gpu();
70 }
71
72 self.frame_timer = FrameTimer::new(self.config.target_fps);
73 self.initialized = true;
74
75 Ok(())
76 }
77
78 fn detect_gpu() -> Option<DeviceInfo> {
80 #[cfg(feature = "cuda")]
82 {
83 Some(DeviceInfo {
84 name: "CUDA Device".to_string(),
85 backend: GpuBackend::Cuda,
86 memory_mb: 8192,
87 compute_capability: "8.6".to_string(),
88 })
89 }
90
91 #[cfg(not(feature = "cuda"))]
92 {
93 None
94 }
95 }
96
97 pub fn has_gpu(&self) -> bool {
99 self.device_info.is_some()
100 }
101
102 pub fn device_info(&self) -> Option<&DeviceInfo> {
104 self.device_info.as_ref()
105 }
106
107 pub fn backend_name(&self) -> &'static str {
109 match &self.device_info {
110 Some(info) => info.backend.name(),
111 None => "CPU",
112 }
113 }
114
115 pub fn begin_frame(&mut self) {
117 self.frame_timer.begin_frame();
118 }
119
120 pub fn end_frame(&mut self) -> FrameTiming {
122 self.frame_timer.end_frame()
123 }
124
125 pub fn target_frame_duration(&self) -> Duration {
127 Duration::from_secs_f64(1.0 / self.config.target_fps as f64)
128 }
129
130 pub fn should_skip_frame(&self) -> bool {
132 self.frame_timer.should_skip()
133 }
134
135 pub fn current_fps(&self) -> f32 {
137 self.frame_timer.current_fps()
138 }
139}
140
141#[derive(Debug, Clone)]
143pub struct DeviceInfo {
144 pub name: String,
146 pub backend: GpuBackend,
148 pub memory_mb: u32,
150 pub compute_capability: String,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum GpuBackend {
157 Cuda,
159 Wgpu,
161 Metal,
163 Cpu,
165}
166
167impl GpuBackend {
168 pub fn name(&self) -> &'static str {
170 match self {
171 GpuBackend::Cuda => "CUDA",
172 GpuBackend::Wgpu => "WebGPU",
173 GpuBackend::Metal => "Metal",
174 GpuBackend::Cpu => "CPU",
175 }
176 }
177}
178
179#[derive(Debug)]
181pub struct FrameTimer {
182 target_fps: u32,
184 frame_start: Instant,
186 last_frame_duration: Duration,
188 smoothed_fps: f32,
190 frame_count: u64,
192 accumulated_time: Duration,
194}
195
196impl FrameTimer {
197 pub fn new(target_fps: u32) -> Self {
199 Self {
200 target_fps,
201 frame_start: Instant::now(),
202 last_frame_duration: Duration::ZERO,
203 smoothed_fps: target_fps as f32,
204 frame_count: 0,
205 accumulated_time: Duration::ZERO,
206 }
207 }
208
209 pub fn begin_frame(&mut self) {
211 self.frame_start = Instant::now();
212 }
213
214 pub fn end_frame(&mut self) -> FrameTiming {
216 self.last_frame_duration = self.frame_start.elapsed();
217 self.accumulated_time += self.last_frame_duration;
218 self.frame_count += 1;
219
220 if self.accumulated_time >= Duration::from_secs(1) {
222 self.smoothed_fps = self.frame_count as f32 / self.accumulated_time.as_secs_f32();
223 self.accumulated_time = Duration::ZERO;
224 self.frame_count = 0;
225 }
226
227 FrameTiming {
228 duration: self.last_frame_duration,
229 fps: self.smoothed_fps,
230 frame_budget_used: self.last_frame_duration.as_secs_f32()
231 / (1.0 / self.target_fps as f32),
232 }
233 }
234
235 pub fn should_skip(&self) -> bool {
237 let target_duration = Duration::from_secs_f64(1.0 / self.target_fps as f64);
238 self.last_frame_duration > target_duration * 2
239 }
240
241 pub fn current_fps(&self) -> f32 {
243 self.smoothed_fps
244 }
245}
246
247#[derive(Debug, Clone, Copy)]
249pub struct FrameTiming {
250 pub duration: Duration,
252 pub fps: f32,
254 pub frame_budget_used: f32,
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_runtime_creation() {
264 let runtime = GpuActorRuntime::new(RuntimeConfig::default());
265 assert!(!runtime.initialized);
266 }
267
268 #[test]
269 fn test_runtime_init() {
270 let mut runtime = GpuActorRuntime::new(RuntimeConfig::default());
271 let result = runtime.init();
272 assert!(result.is_ok());
273 assert!(runtime.initialized);
274 }
275
276 #[test]
277 fn test_frame_timer() {
278 let mut timer = FrameTimer::new(60);
279 timer.begin_frame();
280 std::thread::sleep(Duration::from_millis(10));
281 let timing = timer.end_frame();
282 assert!(timing.duration >= Duration::from_millis(10));
283 }
284}