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§
- Guarded
Resource ScopeGuardtailored for Vulkan
Traits§
- Destroyable
- Indicates that a type is destroyable
- Device
Ext - Extension trait adding guarded methods to
ash::Device - Entry
Ext - Extension trait adding guarded methods to
ash::Entry - Instance
Ext - Extension trait adding guarded methods to
ash::Instance
Type Aliases§
- Guarded
- Most common usecase for
GuardedResource