aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src/ffi/resources.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 07:31:19 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 07:31:19 +0300
commit4b741fb19c495a29bc12f6f057df01c91c1d9097 (patch)
tree02983585bfdec0b14f84d02bed99d3f7a9eb1f62 /adapters/fparkan-render-vulkan/src/ffi/resources.rs
parent645eca551004f9eea8e2276e9110f3a66a1ddc69 (diff)
downloadfparkan-4b741fb19c495a29bc12f6f057df01c91c1d9097.tar.xz
fparkan-4b741fb19c495a29bc12f6f057df01c91c1d9097.zip
feat(vulkan): add depth attachment pipeline support
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi/resources.rs')
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/resources.rs167
1 files changed, 167 insertions, 0 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/resources.rs b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
index 089e759..2318b3b 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/resources.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
@@ -1,11 +1,13 @@
#![allow(unsafe_code)]
use ash::vk;
+use fparkan_platform::DepthStencilSupport;
use super::{
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanStaticMesh,
VulkanStaticTexture,
};
+use crate::select_depth_stencil_attachment_format;
pub(super) struct VulkanAllocatedBuffer {
pub(super) buffer: vk::Buffer,
@@ -18,6 +20,171 @@ pub(super) struct VulkanAllocatedImage {
pub(super) view: vk::ImageView,
}
+/// Depth/stencil image and the exact format selected for its render pass.
+pub(super) struct VulkanDepthAttachment {
+ pub(super) image: VulkanAllocatedImage,
+ pub(super) format: vk::Format,
+}
+
+#[allow(clippy::too_many_lines)]
+pub(super) fn create_depth_attachment(
+ instance: &VulkanInstanceProbe,
+ device: &VulkanLogicalDeviceProbe,
+ extent: (u32, u32),
+ request: DepthStencilSupport,
+) -> Result<VulkanDepthAttachment, VulkanSmokeRendererError> {
+ let supported_formats = depth_attachment_formats(instance, device);
+ let format = select_depth_stencil_attachment_format(&supported_formats, request)
+ .map(vk::Format::from_raw)
+ .ok_or(VulkanSmokeRendererError::InvalidStaticMesh {
+ context: "logical device has no selected depth attachment format",
+ })?;
+ let image_info = vk::ImageCreateInfo::default()
+ .image_type(vk::ImageType::TYPE_2D)
+ .format(format)
+ .extent(vk::Extent3D {
+ width: extent.0,
+ height: extent.1,
+ depth: 1,
+ })
+ .mip_levels(1)
+ .array_layers(1)
+ .samples(vk::SampleCountFlags::TYPE_1)
+ .tiling(vk::ImageTiling::OPTIMAL)
+ .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
+ .sharing_mode(vk::SharingMode::EXCLUSIVE)
+ .initial_layout(vk::ImageLayout::UNDEFINED);
+ // SAFETY: The creation info is stack-owned and uses the live non-zero swapchain extent.
+ let image = unsafe { device.device().create_image(&image_info, None) }.map_err(|result| {
+ VulkanSmokeRendererError::VulkanOperation {
+ context: "vkCreateImage(depth attachment)",
+ result,
+ }
+ })?;
+ // SAFETY: The newly created image belongs to this device.
+ let requirements = unsafe { device.device().get_image_memory_requirements(image) };
+ let Some(memory_type_index) = find_memory_type(
+ instance,
+ device.physical_device(),
+ requirements.memory_type_bits,
+ vk::MemoryPropertyFlags::DEVICE_LOCAL,
+ ) else {
+ // SAFETY: The unbound image is rolled back on its owning device.
+ unsafe { device.device().destroy_image(image, None) };
+ return Err(VulkanSmokeRendererError::MissingMemoryType {
+ context: "depth attachment",
+ });
+ };
+ let allocation = vk::MemoryAllocateInfo::default()
+ .allocation_size(requirements.size)
+ .memory_type_index(memory_type_index);
+ // SAFETY: Allocation parameters are derived from this image's requirements.
+ let allocation_result = unsafe { device.device().allocate_memory(&allocation, None) };
+ let memory = match allocation_result {
+ Ok(memory) => memory,
+ Err(result) => {
+ // SAFETY: The unbound image is rolled back on allocation failure.
+ unsafe { device.device().destroy_image(image, None) };
+ return Err(VulkanSmokeRendererError::VulkanOperation {
+ context: "vkAllocateMemory(depth attachment)",
+ result,
+ });
+ }
+ };
+ // SAFETY: The allocation satisfies this image's queried requirements.
+ if let Err(result) = unsafe { device.device().bind_image_memory(image, memory, 0) } {
+ // SAFETY: Both newly created resources are rolled back together.
+ unsafe {
+ device.device().destroy_image(image, None);
+ device.device().free_memory(memory, None);
+ }
+ return Err(VulkanSmokeRendererError::VulkanOperation {
+ context: "vkBindImageMemory(depth attachment)",
+ result,
+ });
+ }
+ let range = vk::ImageSubresourceRange::default()
+ .aspect_mask(depth_aspect(format))
+ .base_mip_level(0)
+ .level_count(1)
+ .base_array_layer(0)
+ .layer_count(1);
+ let view_info = vk::ImageViewCreateInfo::default()
+ .image(image)
+ .view_type(vk::ImageViewType::TYPE_2D)
+ .format(format)
+ .subresource_range(range);
+ // SAFETY: The image is bound and the depth/stencil aspect matches its selected format.
+ let view_result = unsafe { device.device().create_image_view(&view_info, None) };
+ let view = match view_result {
+ Ok(view) => view,
+ Err(result) => {
+ // SAFETY: Both newly created resources are rolled back together.
+ unsafe {
+ device.device().destroy_image(image, None);
+ device.device().free_memory(memory, None);
+ }
+ return Err(VulkanSmokeRendererError::VulkanOperation {
+ context: "vkCreateImageView(depth attachment)",
+ result,
+ });
+ }
+ };
+ Ok(VulkanDepthAttachment {
+ image: VulkanAllocatedImage {
+ image,
+ memory,
+ view,
+ },
+ format,
+ })
+}
+
+pub(super) fn destroy_depth_attachment(
+ device: &VulkanLogicalDeviceProbe,
+ attachment: &VulkanDepthAttachment,
+) {
+ destroy_allocated_image(device, &attachment.image);
+}
+
+fn depth_attachment_formats(
+ instance: &VulkanInstanceProbe,
+ device: &VulkanLogicalDeviceProbe,
+) -> Vec<i32> {
+ [
+ vk::Format::D16_UNORM,
+ vk::Format::X8_D24_UNORM_PACK32,
+ vk::Format::D32_SFLOAT,
+ vk::Format::D16_UNORM_S8_UINT,
+ vk::Format::D24_UNORM_S8_UINT,
+ vk::Format::D32_SFLOAT_S8_UINT,
+ ]
+ .into_iter()
+ .filter(|format| {
+ // SAFETY: The physical device belongs to this instance and the query returns a value copy.
+ unsafe {
+ instance
+ .instance
+ .get_physical_device_format_properties(device.physical_device(), *format)
+ }
+ .optimal_tiling_features
+ .contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
+ })
+ .map(vk::Format::as_raw)
+ .collect()
+}
+
+fn depth_aspect(format: vk::Format) -> vk::ImageAspectFlags {
+ match format {
+ vk::Format::D16_UNORM_S8_UINT
+ | vk::Format::D24_UNORM_S8_UINT
+ | vk::Format::D32_SFLOAT_S8_UINT => {
+ vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL
+ }
+ _ => vk::ImageAspectFlags::DEPTH,
+ }
+}
+
pub(super) struct VulkanFrameSync {
pub(super) image_available: vk::Semaphore,
pub(super) render_finished: vk::Semaphore,