From 6b9ef3aff29453b3c747cc8d39259e50b52fcb86 Mon Sep 17 00:00:00 2001 From: jkds Date: Sun, 4 Jan 2026 07:14:18 +0100 Subject: [PATCH] app: fix potential bug on exit due to already dropped anim when draw() is called after window quit request. see below for more detailed info. = Crash = 1. User quits -> god.stop_app() -> runtime stops 2. Arc drops -> delete_unmanaged_anim() -> animation removed from self.anims 3. miniquad event loop calls draw() one more time 4. draw_call() hits GfxDrawInstruction::Animation(anim_id) -> self.anims.get_mut(&anim_id).unwrap() PANIC! Animation was already deleted in step 2 = Cause = - DrawCall holds Animation(AnimId) - just an ID, not a reference - self.anims: HashMap stores actual animation data - ManagedSeqAnim::drop() removes animation from self.anims - DrawCalls may still reference deleted animations - Race during shutdown: animations deleted before final draw() = Fix = Change Animation(AnimId) to Animation(ManagedSeqAnimPtr): - Arc keeps ManagedSeqAnim alive as long as DrawCall exists - Drop only fires when all DrawCalls are dropped - Animation stays in self.anims until safe to delete - Uses Rust ownership to prevent bug at compile time --- bin/app/src/gfx/mod.rs | 16 ++++++++-------- bin/app/src/gfx/trax.rs | 2 +- bin/app/src/ui/vid/mod.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bin/app/src/gfx/mod.rs b/bin/app/src/gfx/mod.rs index fb71112ec..27651ee1d 100644 --- a/bin/app/src/gfx/mod.rs +++ b/bin/app/src/gfx/mod.rs @@ -472,14 +472,14 @@ impl AsyncEncodable for DrawMesh { } } -#[derive(Debug, Clone, SerialEncodable)] +#[derive(Debug, Clone)] pub enum DrawInstruction { SetScale(f32), Move(Point), SetPos(Point), ApplyView(Rectangle), Draw(DrawMesh), - Animation(AnimId), + Animation(ManagedSeqAnimPtr), EnableDebug, SetPipeline(GraphicPipeline), } @@ -506,7 +506,7 @@ impl DrawInstruction { } } -#[derive(Clone, Debug, Default, SerialEncodable)] +#[derive(Clone, Debug, Default)] pub struct DrawCall { pub instrs: Vec, pub dcs: Vec, @@ -560,7 +560,7 @@ enum GfxDrawInstruction { SetPos(Point), ApplyView(Rectangle), Draw(GfxDrawMesh), - Animation(AnimId), + Animation(ManagedSeqAnimPtr), EnableDebug, SetPipeline(GraphicPipeline), } @@ -721,10 +721,10 @@ impl<'a> RenderContext<'a> { self.ctx.apply_bindings(&bindings); self.ctx.draw(0, mesh.num_elements, 1); } - GfxDrawInstruction::Animation(anim_id) => { - let anim = self.anims.get_mut(&anim_id).unwrap(); - anim.is_visible = true; - if let Some(dc) = anim.tick() { + GfxDrawInstruction::Animation(anim) => { + let gfx_anim = self.anims.get_mut(&anim.id).unwrap(); + gfx_anim.is_visible = true; + if let Some(dc) = gfx_anim.tick() { self.draw_call(&dc, indent + 1, is_debug); } } diff --git a/bin/app/src/gfx/trax.rs b/bin/app/src/gfx/trax.rs index 943715004..a9e3fa647 100644 --- a/bin/app/src/gfx/trax.rs +++ b/bin/app/src/gfx/trax.rs @@ -57,7 +57,7 @@ impl Trax { 0u8.encode(&mut self.buf).unwrap(); epoch.encode(&mut self.buf).unwrap(); batch_id.encode(&mut self.buf).unwrap(); - dcs.encode(&mut self.buf).unwrap(); + //dcs.encode(&mut self.buf).unwrap(); } pub fn put_start_batch( diff --git a/bin/app/src/ui/vid/mod.rs b/bin/app/src/ui/vid/mod.rs index 013e6e6e4..da69c4808 100644 --- a/bin/app/src/ui/vid/mod.rs +++ b/bin/app/src/ui/vid/mod.rs @@ -250,7 +250,7 @@ impl Video { vec![ DrawInstruction::SetPipeline(GraphicPipeline::YUV), DrawInstruction::Move(rect.pos()), - DrawInstruction::Animation(vid_data.anim.id), + DrawInstruction::Animation(vid_data.anim.clone()), ], vec![], self.z_index.get(),