From 4b741fb19c495a29bc12f6f057df01c91c1d9097 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 18 Jul 2026 08:31:19 +0400 Subject: feat(vulkan): add depth attachment pipeline support --- .../src/ffi/swapchain_resources.rs | 85 ++++++++++++++++++---- 1 file changed, 71 insertions(+), 14 deletions(-) (limited to 'adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs') diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs index 07c2869..ff797e1 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs @@ -1,19 +1,22 @@ #![allow(unsafe_code)] use ash::vk; +use fparkan_platform::DepthStencilSupport; use fparkan_render::{ LegacyBlendMode, LegacyCullMode, LegacyDepthMode, LegacyPipelineState, PipelineKey, }; use std::collections::BTreeMap; use super::{ - color_subresource_range, VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanLogicalDeviceProbe, - VulkanSmokeRendererError, VulkanSwapchainProbe, TRIANGLE_FRAGMENT_SHADER_WORDS, - TRIANGLE_VERTEX_SHADER_WORDS, + color_subresource_range, create_depth_attachment, destroy_depth_attachment, + VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanDepthAttachment, VulkanInstanceProbe, + VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanSwapchainProbe, + TRIANGLE_FRAGMENT_SHADER_WORDS, TRIANGLE_VERTEX_SHADER_WORDS, }; pub(super) struct VulkanSwapchainResources { pub(super) image_views: Vec, + pub(super) depth_attachment: VulkanDepthAttachment, pub(super) render_pass: vk::RenderPass, pub(super) pipeline_layout: vk::PipelineLayout, pub(super) descriptor_set_layout: vk::DescriptorSetLayout, @@ -28,6 +31,7 @@ pub(super) struct VulkanSwapchainResources { struct PartialSwapchainResources { image_views: Vec, + depth_attachment: Option, render_pass: Option, pipeline_layout: Option, descriptor_set_layout: Option, @@ -38,8 +42,9 @@ struct PartialSwapchainResources { command_buffers: Vec, } -#[allow(clippy::too_many_arguments)] +#[allow(clippy::too_many_arguments, clippy::too_many_lines)] pub(super) fn create_swapchain_resources( + instance: &VulkanInstanceProbe, device: &VulkanLogicalDeviceProbe, swapchain: &VulkanSwapchainProbe, command_pool: vk::CommandPool, @@ -47,6 +52,7 @@ pub(super) fn create_swapchain_resources( index_buffer: &VulkanAllocatedBuffer, textures: &[VulkanAllocatedImage], draw_ranges: &[super::VulkanStaticDrawRange], + depth_request: DepthStencilSupport, reuse_command_pool: bool, ) -> Result { // SAFETY: The swapchain is live and owned by this renderer for the duration of the query. @@ -65,6 +71,7 @@ pub(super) fn create_swapchain_resources( &images, swapchain.report.plan.format.format, )?, + depth_attachment: None, render_pass: None, pipeline_layout: None, descriptor_set_layout: None, @@ -75,6 +82,7 @@ pub(super) fn create_swapchain_resources( command_buffers: Vec::new(), }; let ( + depth_attachment, render_pass, pipeline_layout, pipelines, @@ -83,11 +91,13 @@ pub(super) fn create_swapchain_resources( descriptor_sets, sampler, ) = match create_swapchain_pipeline_bundle( + instance, device, swapchain.report.plan.format.format, swapchain.report.plan.extent, textures, draw_ranges, + depth_request, ) { Ok(bundle) => bundle, Err(error) => { @@ -95,6 +105,8 @@ pub(super) fn create_swapchain_resources( return Err(error); } }; + let depth_view = depth_attachment.image.view; + partial.depth_attachment = Some(depth_attachment); partial.render_pass = Some(render_pass); partial.pipeline_layout = Some(pipeline_layout); partial.descriptor_set_layout = Some(descriptor_set_layout); @@ -105,6 +117,7 @@ pub(super) fn create_swapchain_resources( device, render_pass, &partial.image_views, + depth_view, swapchain.report.plan.extent, ) { Ok(framebuffers) => framebuffers, @@ -128,8 +141,16 @@ pub(super) fn create_swapchain_resources( }; partial.command_buffers = command_buffers; let _ = (vertex_buffer, index_buffer); + let depth_attachment = + partial + .depth_attachment + .take() + .ok_or(VulkanSmokeRendererError::InvariantViolation { + context: "depth attachment ownership after swapchain setup", + })?; Ok(VulkanSwapchainResources { image_views: partial.image_views, + depth_attachment, render_pass, pipeline_layout, descriptor_set_layout, @@ -156,13 +177,16 @@ fn create_swapchain_image_views( #[allow(clippy::type_complexity)] fn create_swapchain_pipeline_bundle( + instance: &VulkanInstanceProbe, device: &VulkanLogicalDeviceProbe, format: i32, extent: (u32, u32), textures: &[VulkanAllocatedImage], draw_ranges: &[super::VulkanStaticDrawRange], + depth_request: DepthStencilSupport, ) -> Result< ( + VulkanDepthAttachment, vk::RenderPass, vk::PipelineLayout, BTreeMap, @@ -173,11 +197,19 @@ fn create_swapchain_pipeline_bundle( ), VulkanSmokeRendererError, > { - let render_pass = create_render_pass(device, format)?; + let depth_attachment = create_depth_attachment(instance, device, extent, depth_request)?; + let render_pass = match create_render_pass(device, format, depth_attachment.format) { + Ok(render_pass) => render_pass, + Err(error) => { + destroy_depth_attachment(device, &depth_attachment); + return Err(error); + } + }; let (descriptor_set_layout, descriptor_pool, descriptor_sets, sampler) = create_texture_descriptor_bundle(device, textures).inspect_err(|_| { // SAFETY: The render pass was created above on this live logical device and is destroyed on setup failure. unsafe { device.device().destroy_render_pass(render_pass, None) }; + destroy_depth_attachment(device, &depth_attachment); })?; let pipeline_layout = create_pipeline_layout(device, descriptor_set_layout).inspect_err(|_| { @@ -192,6 +224,7 @@ fn create_swapchain_pipeline_bundle( .destroy_descriptor_set_layout(descriptor_set_layout, None); device.device().destroy_render_pass(render_pass, None); } + destroy_depth_attachment(device, &depth_attachment); })?; let pipelines = create_graphics_pipeline_cache(device, render_pass, pipeline_layout, extent, draw_ranges) @@ -210,8 +243,10 @@ fn create_swapchain_pipeline_bundle( .destroy_descriptor_set_layout(descriptor_set_layout, None); device.device().destroy_render_pass(render_pass, None); } + destroy_depth_attachment(device, &depth_attachment); })?; Ok(( + depth_attachment, render_pass, pipeline_layout, pipelines, @@ -226,11 +261,12 @@ fn create_swapchain_framebuffers( device: &VulkanLogicalDeviceProbe, render_pass: vk::RenderPass, image_views: &[vk::ImageView], + depth_view: vk::ImageView, extent: (u32, u32), ) -> Result, VulkanSmokeRendererError> { let mut framebuffers = Vec::with_capacity(image_views.len()); for image_view in image_views.iter().copied() { - match create_framebuffer(device, render_pass, image_view, extent) { + match create_framebuffer(device, render_pass, image_view, depth_view, extent) { Ok(framebuffer) => framebuffers.push(framebuffer), Err(error) => { // SAFETY: These framebuffers were created above on this live logical device and are destroyed on setup failure. @@ -288,6 +324,7 @@ fn create_image_view( fn create_render_pass( device: &VulkanLogicalDeviceProbe, format: i32, + depth_format: vk::Format, ) -> Result { let color_attachment = vk::AttachmentDescription::default() .format(vk::Format::from_raw(format)) @@ -299,10 +336,23 @@ fn create_render_pass( let color_attachment_ref = vk::AttachmentReference::default() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); + let depth_attachment = vk::AttachmentDescription::default() + .format(depth_format) + .samples(vk::SampleCountFlags::TYPE_1) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::DONT_CARE) + .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) + .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) + .initial_layout(vk::ImageLayout::UNDEFINED) + .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + let depth_attachment_ref = vk::AttachmentReference::default() + .attachment(1) + .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); let color_attachments = [color_attachment_ref]; let subpass = vk::SubpassDescription::default() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) - .color_attachments(&color_attachments); + .color_attachments(&color_attachments) + .depth_stencil_attachment(&depth_attachment_ref); let dependency = vk::SubpassDependency::default() .src_subpass(vk::SUBPASS_EXTERNAL) .dst_subpass(0) @@ -310,7 +360,7 @@ fn create_render_pass( .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .src_access_mask(vk::AccessFlags::empty()) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE); - let attachments = [color_attachment]; + let attachments = [color_attachment, depth_attachment]; let subpasses = [subpass]; let dependencies = [dependency]; let create_info = vk::RenderPassCreateInfo::default() @@ -512,11 +562,6 @@ fn create_graphics_pipeline( extent: (u32, u32), state: LegacyPipelineState, ) -> Result { - if state.depth != LegacyDepthMode::Disabled { - return Err(VulkanSmokeRendererError::InvalidStaticMesh { - context: "static renderer has no depth attachment for requested pipeline state", - }); - } if state.alpha_test { return Err(VulkanSmokeRendererError::InvalidStaticMesh { context: @@ -604,6 +649,12 @@ fn create_graphics_pipeline( let multisample_state = vk::PipelineMultisampleStateCreateInfo::default() .sample_shading_enable(false) .rasterization_samples(vk::SampleCountFlags::TYPE_1); + let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::default() + .depth_test_enable(state.depth != LegacyDepthMode::Disabled) + .depth_write_enable(state.depth == LegacyDepthMode::TestWrite) + .depth_compare_op(vk::CompareOp::LESS_OR_EQUAL) + .depth_bounds_test_enable(false) + .stencil_test_enable(false); let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default() .color_write_mask( vk::ColorComponentFlags::R @@ -629,6 +680,7 @@ fn create_graphics_pipeline( .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) + .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .layout(pipeline_layout) .render_pass(render_pass) @@ -671,9 +723,10 @@ fn create_framebuffer( device: &VulkanLogicalDeviceProbe, render_pass: vk::RenderPass, image_view: vk::ImageView, + depth_view: vk::ImageView, extent: (u32, u32), ) -> Result { - let attachments = [image_view]; + let attachments = [image_view, depth_view]; let create_info = vk::FramebufferCreateInfo::default() .render_pass(render_pass) .attachments(&attachments) @@ -738,6 +791,7 @@ pub(super) fn destroy_swapchain_resources( device .device() .destroy_render_pass(resources.render_pass, None); + destroy_depth_attachment(device, &resources.depth_attachment); for image_view in resources.image_views { device.device().destroy_image_view(image_view, None); } @@ -783,6 +837,9 @@ fn destroy_partial_swapchain_resources( if let Some(render_pass) = partial.render_pass { device.device().destroy_render_pass(render_pass, None); } + if let Some(depth_attachment) = partial.depth_attachment { + destroy_depth_attachment(device, &depth_attachment); + } for image_view in partial.image_views { device.device().destroy_image_view(image_view, None); } -- cgit v1.2.3