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;
11
 use crate::media::{scale_image_fast, AnimatedGif};
11
 use crate::media::{scale_image_fast, AnimatedGif};
12
 use crate::x11::{AnimationRenderer, Connection};
12
 use crate::x11::{AnimationRenderer, Connection};
13
 
13
 
14
+/// Default memory budget for pre-scaled animation frames (256 MB)
15
+const DEFAULT_MEMORY_BUDGET: u64 = 256 * 1024 * 1024;
16
+
14
 /// Configuration for the animation loop
17
 /// Configuration for the animation loop
15
 #[derive(Debug, Clone)]
18
 #[derive(Debug, Clone)]
16
 pub struct AnimationConfig {
19
 pub struct AnimationConfig {
@@ -38,8 +41,13 @@ pub struct AnimationLoop {
38
     gif: AnimatedGif,
41
     gif: AnimatedGif,
39
     /// X11 animation renderer
42
     /// X11 animation renderer
40
     renderer: AnimationRenderer,
43
     renderer: AnimationRenderer,
41
-    /// Pre-scaled frames (cached)
44
+    /// Pre-scaled frames (empty if streaming mode)
42
     scaled_frames: Vec<image::RgbaImage>,
45
     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,
43
     /// Configuration
51
     /// Configuration
44
     config: AnimationConfig,
52
     config: AnimationConfig,
45
     /// Whether the animation is paused
53
     /// Whether the animation is paused
@@ -57,25 +65,42 @@ impl AnimationLoop {
57
     ) -> Result<Self> {
65
     ) -> Result<Self> {
58
         let renderer = AnimationRenderer::new(conn)?;
66
         let renderer = AnimationRenderer::new(conn)?;
59
         let (screen_width, screen_height) = renderer.dimensions();
67
         let (screen_width, screen_height) = renderer.dimensions();
60
-
68
+        let sw = screen_width as u32;
61
-        // Pre-scale all frames
69
+        let sh = screen_height as u32;
62
-        let scaled_frames: Vec<image::RgbaImage> = gif
70
+
63
-            .frames()
71
+        let per_frame_bytes = sw as u64 * sh as u64 * 4;
64
-            .iter()
72
+        let total_bytes = per_frame_bytes * gif.frame_count() as u64;
65
-            .map(|frame| {
73
+
66
-                scale_image_fast(
74
+        let (scaled_frames, streaming) = if total_bytes <= DEFAULT_MEMORY_BUDGET {
67
-                    &frame.image,
75
+            tracing::info!(
68
-                    screen_width as u32,
76
+                "Animation fits in memory budget ({:.1} MB), pre-scaling all {} frames",
69
-                    screen_height as u32,
77
+                total_bytes as f64 / (1024.0 * 1024.0),
70
-                    config.scale_mode,
78
+                gif.frame_count(),
71
-                )
79
+            );
72
-            })
80
+            let frames: Vec<image::RgbaImage> = gif
73
-            .collect();
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
+        };
74
 
96
 
75
         Ok(Self {
97
         Ok(Self {
76
             gif,
98
             gif,
77
             renderer,
99
             renderer,
78
             scaled_frames,
100
             scaled_frames,
101
+            streaming,
102
+            screen_width: sw,
103
+            screen_height: sh,
79
             config,
104
             config,
80
             paused: Arc::new(AtomicBool::new(false)),
105
             paused: Arc::new(AtomicBool::new(false)),
81
             stop: Arc::new(AtomicBool::new(false)),
106
             stop: Arc::new(AtomicBool::new(false)),
@@ -165,8 +190,18 @@ impl AnimationLoop {
165
             }
190
             }
166
 
191
 
167
             // Render current frame
192
             // Render current frame
168
-            let scaled_frame = &self.scaled_frames[frame_index];
193
+            if self.streaming {
169
-            self.renderer.render_and_present(conn, scaled_frame)?;
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
+            }
170
 
205
 
171
             // Get delay for current frame
206
             // Get delay for current frame
172
             let frame_delay = self.gif.frames()[frame_index].delay;
207
             let frame_delay = self.gif.frames()[frame_index].delay;
@@ -199,10 +234,20 @@ impl AnimationLoop {
199
         }
234
         }
200
 
235
 
201
         let frame_index = self.gif.current_index();
236
         let frame_index = self.gif.current_index();
202
-        let scaled_frame = &self.scaled_frames[frame_index];
203
 
237
 
204
         // Render current frame
238
         // 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
+        }
206
 
251
 
207
         // Get delay for current frame
252
         // Get delay for current frame
208
         let delay = self.gif.current_frame().delay;
253
         let delay = self.gif.current_frame().delay;