Crate ashpan[−][src]
Expand description
This crate provides RAII helpers for ash. In particular:
Guarded/GuardedResourceis essentially aScopeGuardthat selects an appropriate destructor automatically.DeviceExt(along withEntryExtandInstanceExt) provide convenience methods to create resources and wrap them inGuardedResources.Destroyableallows you to extend the behavior ofGuardedResource.
Introduction
When working with Vulkan, you generally want to group multiple resources together into a few large structs. Unfortunately, errors during initialization can leak resources:
unsafe fn create_resources(device: &ash::Device) -> VkResult<Resources> { let render_pass = create_render_pass(device)?; // BUG: Failure leaks render_pass let pipeline_layout = create_pipeline_layout(device)?; // BUG: Failure leaks render_pass and pipeline_layout let pipeline = create_pipeline(device, render_pass, pipeline_layout)?; Ok(Resources { render_pass, pipeline_layout, pipeline, }) }
It’s straightforward to solve this with scopeguard, but it
tends to be a bit verbose and repetitive:
use scopeguard::ScopeGuard; type Guarded<'a, T> = ScopeGuard<(T, &'a ash::Device), fn((T, &'a ash::Device))>; unsafe fn create_resources(device: &ash::Device) -> VkResult<Resources> { let render_pass = create_render_pass(device)?; let pipeline_layout = create_pipeline_layout(device)?; let pipeline = create_pipeline(device, render_pass.0, pipeline_layout.0)?; Ok(Resources { render_pass: ScopeGuard::into_inner(render_pass).0, pipeline_layout: ScopeGuard::into_inner(pipeline_layout).0, pipeline: ScopeGuard::into_inner(pipeline).0, }) } unsafe fn create_render_pass(device: &ash::Device) -> VkResult<Guarded<vk::RenderPass>> { let create_info = unimplemented!(); let render_pass = device.create_render_pass(create_info, None)?; Ok(scopeguard::guard( (render_pass, device), |(render_pass, device)| device.destroy_render_pass(render_pass, None), )) } // fn create_pipeline_layout(...) { ... } // fn create_pipeline(...) {...}
ashpan reduces the friction of using scopeguard
with ash by automatically selecting the destructor, passing the same
allocation_callbacks to the destructor that were used for resource creation and making
guarded resources convenient to extract:
use ashpan::{DeviceExt, Guarded}; unsafe fn create_resources(device: &ash::Device) -> VkResult<Resources> { let render_pass = create_render_pass(device)?; let pipeline_layout = create_pipeline_layout(device)?; let pipeline = create_pipeline(device, *render_pass, *pipeline_layout)?; Ok(Resources { render_pass: render_pass.take(), pipeline_layout: pipeline_layout.take(), pipeline: pipeline.take(), }) } unsafe fn create_render_pass(device: &ash::Device) -> VkResult<Guarded<vk::RenderPass>> { let create_info = unimplemented!(); device.create_guarded_render_pass(create_info, None) } // fn create_pipeline_layout(...) { ... } // fn create_pipeline(...) {...}
It’s also possible to extend Guarded to handle application-specific types:
use ashpan::{Destroyable, DeviceExt, Guarded}; impl Destroyable for Resources { type Destroyer = ash::Device; unsafe fn destroy_with( &mut self, device: &ash::Device, allocation_callbacks: Option<&vk::AllocationCallbacks>, ) { device.destroy_pipeline(self.pipeline, allocation_callbacks); device.destroy_pipeline_layout(self.pipeline_layout, allocation_callbacks); device.destroy_render_pass(self.render_pass, allocation_callbacks); } } // Elsewhere... unsafe fn create_resources(device: &ash::Device) -> VkResult<Guarded<Resources>> { let resources = unimplemented!(); Ok(Guarded::new(resources, device, None)) }
Structs
ScopeGuard tailored
for Vulkan
Traits
Indicates that a type is destroyable
Extension trait adding guarded methods to ash::Device
Extension trait adding guarded methods to ash::EntryCustom
Extension trait adding guarded methods to ash::Instance
Type Definitions
Most common usecase for GuardedResource