From 97f56c56ba1f809dd1c1fae8fd238f1e7de4c4b9 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Thu, 25 Jun 2026 07:32:26 +0400 Subject: fix(vulkan-smoke): rollback partial swapchain resources --- adapters/fparkan-render-vulkan/src/ffi/smoke.rs | 83 ++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs index 1707f2b..b911b1e 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs @@ -29,6 +29,44 @@ fn take_runtime_owners_in_dependency_order +where + F: FnOnce(T), +{ + value: Option, + rollback: Option, +} + +impl RollbackOnDrop +where + F: FnOnce(T), +{ + fn new(value: T, rollback: F) -> Self { + Self { + value: Some(value), + rollback: Some(rollback), + } + } + + fn commit(mut self) -> T { + self.rollback.take(); + self.value + .take() + .expect("rollback guard must hold a value until commit") + } +} + +impl Drop for RollbackOnDrop +where + F: FnOnce(T), +{ + fn drop(&mut self) { + if let (Some(value), Some(rollback)) = (self.value.take(), self.rollback.take()) { + rollback(value); + } + } +} + impl VulkanSmokeRenderer { /// Creates a live Vulkan smoke renderer bound to a live native window. /// @@ -395,9 +433,9 @@ impl VulkanSmokeRenderer { &mut self, reuse_command_pool: bool, ) -> Result<(), VulkanSmokeRendererError> { - let resources = { - let device = self.device_ref()?; - let swapchain = self.swapchain_ref()?; + let device = self.device_ref()?; + let swapchain = self.swapchain_ref()?; + let resources = RollbackOnDrop::new( create_swapchain_resources( device, swapchain, @@ -405,14 +443,13 @@ impl VulkanSmokeRenderer { self.vertex_buffer_ref()?, self.index_buffer_ref()?, reuse_command_pool, - )? - }; - let frame_sync = { - let device = self.device_ref()?; - create_frame_sync(device)? - }; + )?, + |resources| destroy_swapchain_resources(device, self.command_pool, resources), + ); + let frame_sync = create_frame_sync(device)?; let swapchain_extent = self.swapchain_ref()?.report.plan.extent; let swapchain_image_count = self.swapchain_ref()?.report.image_count; + let resources = resources.commit(); self.images_in_flight = vec![vk::Fence::null(); resources.image_views.len()]; self.frame_sync = frame_sync; self.report.swapchain_extent = swapchain_extent; @@ -578,7 +615,7 @@ impl Drop for VulkanSmokeRenderer { #[cfg(test)] mod tests { - use super::take_runtime_owners_in_dependency_order; + use super::{take_runtime_owners_in_dependency_order, RollbackOnDrop}; use std::cell::RefCell; use std::rc::Rc; @@ -713,4 +750,30 @@ mod tests { assert_eq!(record_teardown_steps(&present_steps), expected); } } + + #[test] + fn rollback_guard_runs_cleanup_when_later_step_fails() { + let log = Rc::new(RefCell::new(Vec::new())); + { + let _guard = RollbackOnDrop::new(tracker(TeardownStep::Swapchain, &log), |tracker| { + drop(tracker) + }); + } + + assert_eq!(log.borrow().as_slice(), &[TeardownStep::Swapchain]); + } + + #[test] + fn rollback_guard_skips_cleanup_after_commit() { + let log = Rc::new(RefCell::new(Vec::new())); + let tracker = RollbackOnDrop::new(tracker(TeardownStep::Swapchain, &log), |tracker| { + drop(tracker) + }) + .commit(); + assert!(log.borrow().is_empty()); + + drop(tracker); + + assert_eq!(log.borrow().as_slice(), &[TeardownStep::Swapchain]); + } } -- cgit v1.2.3