gardesk/garbg / 4f987ab

Browse files

apply memory budget to AnimationLoop CLI path to prevent OOM

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
4f987aba17b4c03229da1340cd6721eab9276f55
Parents
901b4c8
Tree
c9592b4

1 changed file

StatusFile+-
M garbg/src/daemon/animation_loop.rs 64 19
garbg/src/daemon/animation_loop.rsmodified
@@ -11,6 +11,9 @@ use crate::config::ScaleMode;
1111
 use crate::media::{scale_image_fast, AnimatedGif};
1212
 use crate::x11::{AnimationRenderer, Connection};
1313
 
14
+/// Default memory budget for pre-scaled animation frames (256 MB)
15
+const DEFAULT_MEMORY_BUDGET: u64 = 256 * 1024 * 1024;
16
+
1417
 /// Configuration for the animation loop
1518
 #[derive(Debug, Clone)]
1619
 pub struct AnimationConfig {
@@ -38,8 +41,13 @@ pub struct AnimationLoop {
3841
     gif: AnimatedGif,
3942
     /// X11 animation renderer
4043
     renderer: AnimationRenderer,
41
-    /// Pre-scaled frames (cached)
44
+    /// Pre-scaled frames (empty if streaming mode)
4245
     scaled_frames: Vec<image::RgbaImage>,
46
+    /// Whether to scale frames on-the-fly instead of pre-scaling
47
+    streaming: bool,
48
+    /// Screen dimensions (used for on-the-fly scaling)
49
+    screen_width: u32,
50
+    screen_height: u32,
4351
     /// Configuration
4452
     config: AnimationConfig,
4553
     /// Whether the animation is paused
@@ -57,25 +65,42 @@ impl AnimationLoop {
5765
     ) -> Result<Self> {
5866
         let renderer = AnimationRenderer::new(conn)?;
5967
         let (screen_width, screen_height) = renderer.dimensions();
60
-
61
-        // Pre-scale all frames
62
-        let scaled_frames: Vec<image::RgbaImage> = gif
63
-            .frames()
64
-            .iter()
65
-            .map(|frame| {
66
-                scale_image_fast(
67
-                    &frame.image,
68
-                    screen_width as u32,
69
-                    screen_height as u32,
70
-                    config.scale_mode,
71
-                )
72
-            })
73
-            .collect();
68
+        let sw = screen_width as u32;
69
+        let sh = screen_height as u32;
70
+
71
+        let per_frame_bytes = sw as u64 * sh as u64 * 4;
72
+        let total_bytes = per_frame_bytes * gif.frame_count() as u64;
73
+
74
+        let (scaled_frames, streaming) = if total_bytes <= DEFAULT_MEMORY_BUDGET {
75
+            tracing::info!(
76
+                "Animation fits in memory budget ({:.1} MB), pre-scaling all {} frames",
77
+                total_bytes as f64 / (1024.0 * 1024.0),
78
+                gif.frame_count(),
79
+            );
80
+            let frames: Vec<image::RgbaImage> = gif
81
+                .frames()
82
+                .iter()
83
+                .map(|frame| {
84
+                    scale_image_fast(&frame.image, sw, sh, config.scale_mode)
85
+                })
86
+                .collect();
87
+            (frames, false)
88
+        } else {
89
+            tracing::info!(
90
+                "Animation exceeds memory budget ({:.1} MB > {:.1} MB), scaling frames on-the-fly",
91
+                total_bytes as f64 / (1024.0 * 1024.0),
92
+                DEFAULT_MEMORY_BUDGET as f64 / (1024.0 * 1024.0),
93
+            );
94
+            (Vec::new(), true)
95
+        };
7496
 
7597
         Ok(Self {
7698
             gif,
7799
             renderer,
78100
             scaled_frames,
101
+            streaming,
102
+            screen_width: sw,
103
+            screen_height: sh,
79104
             config,
80105
             paused: Arc::new(AtomicBool::new(false)),
81106
             stop: Arc::new(AtomicBool::new(false)),
@@ -165,8 +190,18 @@ impl AnimationLoop {
165190
             }
166191
 
167192
             // Render current frame
168
-            let scaled_frame = &self.scaled_frames[frame_index];
169
-            self.renderer.render_and_present(conn, scaled_frame)?;
193
+            if self.streaming {
194
+                let scaled = scale_image_fast(
195
+                    &self.gif.frames()[frame_index].image,
196
+                    self.screen_width,
197
+                    self.screen_height,
198
+                    self.config.scale_mode,
199
+                );
200
+                self.renderer.render_and_present(conn, &scaled)?;
201
+            } else {
202
+                let scaled_frame = &self.scaled_frames[frame_index];
203
+                self.renderer.render_and_present(conn, scaled_frame)?;
204
+            }
170205
 
171206
             // Get delay for current frame
172207
             let frame_delay = self.gif.frames()[frame_index].delay;
@@ -199,10 +234,20 @@ impl AnimationLoop {
199234
         }
200235
 
201236
         let frame_index = self.gif.current_index();
202
-        let scaled_frame = &self.scaled_frames[frame_index];
203237
 
204238
         // Render current frame
205
-        self.renderer.render_and_present(conn, scaled_frame)?;
239
+        if self.streaming {
240
+            let scaled = scale_image_fast(
241
+                &self.gif.frames()[frame_index].image,
242
+                self.screen_width,
243
+                self.screen_height,
244
+                self.config.scale_mode,
245
+            );
246
+            self.renderer.render_and_present(conn, &scaled)?;
247
+        } else {
248
+            let scaled_frame = &self.scaled_frames[frame_index];
249
+            self.renderer.render_and_present(conn, scaled_frame)?;
250
+        }
206251
 
207252
         // Get delay for current frame
208253
         let delay = self.gif.current_frame().delay;