From 912f828aa3e66cbc3ec3ad41996bb3dec800ea9d Mon Sep 17 00:00:00 2001 From: midasmatterhorn <248507632+midasmatterhorn@users.noreply.github.com> Date: Thu, 15 Oct 2026 16:14:47 +0300 Subject: [PATCH] project: output_size never asks for sides past the comp limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A 30 000 px comp (the repaired MAX_SIZE on load) at resolution 4.0 produced 120 000 px sides, which overflow u32 in the exporters' w * h * 4 (debug panic, release wrap then slice panic) after already requesting a ~57 GB frame. The resize path in the same file clamps to 30 000; output_size now does too. Regression test: output_size_never_exceeds_the_comp_limit — on main it returns (120000, 120000); with the clamp (30000, 30000), through both RenderSettings and the OutputModule wrapper. --- crates/project/src/render_queue.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/project/src/render_queue.rs b/crates/project/src/render_queue.rs index 2d3ab741b..e49c5d1cc 100644 --- a/crates/project/src/render_queue.rs +++ b/crates/project/src/render_queue.rs @@ -444,7 +444,11 @@ impl RenderSettings { } pub fn output_size(&self, comp: &Comp) -> (u32, u32) { let s = self.resolution.clamp(0.01, 4.0); - (((comp.width as f64 * s).round() as u32).max(1), ((comp.height as f64 * s).round() as u32).max(1)) + // A 30 000 px comp at 4x would otherwise request 120 000 px sides, overflowing + // `w * h * 4` in the exporters. The resize path clamps to the same limit. + let w = ((comp.width as f64 * s).round() as u32).max(1).min(30_000); + let h = ((comp.height as f64 * s).round() as u32).max(1).min(30_000); + (w, h) } pub fn resolution_label(&self) -> &'static str { match self.resolution { @@ -1663,6 +1667,16 @@ mod tests { assert_eq!(m.output_size(&c, &half), (400, 200)); } + #[test] + fn output_size_never_exceeds_the_comp_limit() { + // 30 000 px at 4x must not request 120 000 px sides (exporter `w * h * 4`). + let c = Comp::new(30_000, 30_000, FrameRate::new(30, 1), Tick::from_seconds_f64(1.0)); + let rs = RenderSettings { resolution: 4.0, ..Default::default() }; + assert_eq!(rs.output_size(&c), (30_000, 30_000)); + let m = OutputModule::for_format(OutputFormat::PngSequence); + assert_eq!(m.output_size(&c, &rs), (30_000, 30_000)); + } + #[test] fn uncompressed_quicktime_format() { let f = OutputFormat::from_name("uncompressed").unwrap();