Expand description
This crate provides RAII helpers for ash
. In particular:
Guarded
/GuardedResource
is essentially aScopeGuard
that selects an appropriate destructor automatically.DeviceExt
(along withEntryExt
andInstanceExt
) provide convenience methods to create resources and wrap them inGuardedResource
s.Destroyable
allows 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 ScopeGuard
tailored 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