use crate::device::VulkanContext;
use crate::render::sample_flags;
use crate::resource::{backend_err, WithDetail};
use ash::vk;
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};
use gpu_allocator::MemoryLocation;
use impeller_hal::{Error, Extent2D, Result};
pub struct StencilBuffer {
pub image: vk::Image,
pub view: vk::ImageView,
allocation: Option<Allocation>,
}
impl StencilBuffer {
pub fn destroy(mut self, ctx: &mut VulkanContext) {
let device = ctx.raw_device().clone();
unsafe {
device.destroy_image_view(self.view, None);
device.destroy_image(self.image, None);
}
if let Some(allocation) = self.allocation.take() {
let _ = ctx.allocator_mut().free(allocation);
}
}
}
pub fn stencil_format(ctx: &VulkanContext) -> Result<vk::Format> {
let candidates = [
vk::Format::S8_UINT,
vk::Format::D24_UNORM_S8_UINT,
vk::Format::D32_SFLOAT_S8_UINT,
];
for format in candidates {
let properties = unsafe {
ctx.raw_instance()
.get_physical_device_format_properties(ctx.raw_physical_device(), format)
};
if properties
.optimal_tiling_features
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
{
return Ok(format);
}
}
Err(Error::Unsupported(
"no stencil attachment format; this device cannot clip by a path",
))
}
pub fn create(
ctx: &mut VulkanContext,
extent: Extent2D,
samples: u32,
format: vk::Format,
) -> Result<StencilBuffer> {
let info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(format)
.extent(vk::Extent3D {
width: extent.width,
height: extent.height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(sample_flags(samples))
.tiling(vk::ImageTiling::OPTIMAL)
.usage(
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT
| vk::ImageUsageFlags::TRANSIENT_ATTACHMENT,
)
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED);
let device = ctx.raw_device().clone();
let image =
unsafe { device.create_image(&info, None) }.map_err(|e| backend_err("create_image", e))?;
let requirements = unsafe { device.get_image_memory_requirements(image) };
let allocation = match ctx.allocator_mut().allocate(&AllocationCreateDesc {
name: "stencil",
requirements,
location: MemoryLocation::GpuOnly,
linear: false,
allocation_scheme: AllocationScheme::GpuAllocatorManaged,
}) {
Ok(allocation) => allocation,
Err(e) => {
unsafe { device.destroy_image(image, None) };
return Err(Error::OutOfMemory {
what: "stencil memory",
}
.with_detail(e));
}
};
if let Err(e) =
unsafe { device.bind_image_memory(image, allocation.memory(), allocation.offset()) }
{
let _ = ctx.allocator_mut().free(allocation);
unsafe { device.destroy_image(image, None) };
return Err(backend_err("bind_image_memory", e));
}
let view_info = vk::ImageViewCreateInfo::default()
.image(image)
.view_type(vk::ImageViewType::TYPE_2D)
.format(format)
.subresource_range(
vk::ImageSubresourceRange::default()
.aspect_mask(aspect_mask(format))
.level_count(1)
.layer_count(1),
);
match unsafe { device.create_image_view(&view_info, None) } {
Ok(view) => Ok(StencilBuffer {
image,
view,
allocation: Some(allocation),
}),
Err(e) => {
let _ = ctx.allocator_mut().free(allocation);
unsafe { device.destroy_image(image, None) };
Err(backend_err("create_image_view", e))
}
}
}
pub fn aspect_mask(format: vk::Format) -> vk::ImageAspectFlags {
match format {
vk::Format::S8_UINT => vk::ImageAspectFlags::STENCIL,
_ => vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
}
}