diff options
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs')
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs | 163 |
1 files changed, 124 insertions, 39 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs index 89ca43a..a44f79d 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs @@ -3,7 +3,8 @@ use ash::vk; use fparkan_platform::DepthStencilSupport; use fparkan_render::{ - LegacyBlendMode, LegacyCullMode, LegacyDepthMode, LegacyPipelineState, PipelineKey, + LegacyBlendMode, LegacyCullMode, LegacyDepthBias, LegacyDepthMode, LegacyPipelineState, + PipelineKey, }; use std::collections::BTreeMap; @@ -173,11 +174,12 @@ pub(super) fn create_swapchain_resources( device: &VulkanLogicalDeviceProbe, swapchain: &VulkanSwapchainProbe, command_pool: vk::CommandPool, + pipeline_cache: vk::PipelineCache, vertex_buffer: &VulkanAllocatedBuffer, index_buffer: &VulkanAllocatedBuffer, textures: &[VulkanAllocatedImage], texture_slot_indices: &[usize], - draw_ranges: &[super::VulkanStaticDrawRange], + base_pipeline_states: &[LegacyPipelineState], depth_request: DepthStencilSupport, reuse_command_pool: bool, ) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> { @@ -280,11 +282,12 @@ pub(super) fn create_swapchain_resources( ) = match create_swapchain_pipeline_bundle( instance, device, + pipeline_cache, swapchain.report.plan.format.format, swapchain.report.plan.extent, textures, texture_slot_indices, - draw_ranges, + base_pipeline_states, depth_request, partial .frame_uniform_buffer @@ -448,11 +451,12 @@ fn create_swapchain_image_views( fn create_swapchain_pipeline_bundle( instance: &VulkanInstanceProbe, device: &VulkanLogicalDeviceProbe, + pipeline_cache: vk::PipelineCache, format: i32, extent: (u32, u32), textures: &[VulkanAllocatedImage], texture_slot_indices: &[usize], - draw_ranges: &[super::VulkanStaticDrawRange], + base_pipeline_states: &[LegacyPipelineState], depth_request: DepthStencilSupport, frame_uniform_buffer: &VulkanAllocatedBuffer, material_specular_buffer: &VulkanAllocatedBuffer, @@ -509,25 +513,31 @@ fn create_swapchain_pipeline_bundle( } destroy_depth_attachment(device, &depth_attachment); })?; - let pipelines = - create_graphics_pipeline_cache(device, render_pass, pipeline_layout, extent, draw_ranges) - .inspect_err(|_| { - // SAFETY: These objects were created above on this live logical device and are destroyed on setup failure. - unsafe { - device - .device() - .destroy_pipeline_layout(pipeline_layout, None); - device.device().destroy_sampler(sampler, None); - device - .device() - .destroy_descriptor_pool(descriptor_pool, None); - device - .device() - .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, + pipeline_cache, + render_pass, + pipeline_layout, + extent, + base_pipeline_states, + ) + .inspect_err(|_| { + // SAFETY: These objects were created above on this live logical device and are destroyed on setup failure. + unsafe { + device + .device() + .destroy_pipeline_layout(pipeline_layout, None); + device.device().destroy_sampler(sampler, None); + device + .device() + .destroy_descriptor_pool(descriptor_pool, None); + device + .device() + .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, @@ -922,29 +932,52 @@ fn create_texture_descriptor_bundle( Ok((layout, pool, descriptor_sets, sampler)) } -fn extent_component_to_f32(value: u32) -> f32 { - value as f32 +pub(super) fn draw_viewport(extent: (u32, u32), material_mode: f32) -> vk::Viewport { + // Ngi32 RVA 0x7170 maps world depth to 0.1..0.99; TL and sky depth bypass it. + let explicit_depth = material_mode.round() as u32 & 12 != 0; + vk::Viewport { + x: 0.0, + y: 0.0, + width: extent.0 as f32, + height: extent.1 as f32, + min_depth: if explicit_depth { 0.0 } else { 0.1 }, + max_depth: if explicit_depth { 1.0 } else { 0.99 }, + } +} + +fn depth_bias_parameters(depth_bias: LegacyDepthBias) -> (bool, f32, f32) { + match depth_bias { + LegacyDepthBias::Disabled => (false, 0.0, 0.0), + // shortcut: Vulkan constantFactor=-1.0/slopeFactor=0.0 is empirical; verify native raster state before claiming parity. + LegacyDepthBias::ProjectedDecal => (true, -1.0, 0.0), + } } fn create_graphics_pipeline_cache( device: &VulkanLogicalDeviceProbe, + pipeline_cache: vk::PipelineCache, render_pass: vk::RenderPass, pipeline_layout: vk::PipelineLayout, extent: (u32, u32), - draw_ranges: &[super::VulkanStaticDrawRange], + base_pipeline_states: &[LegacyPipelineState], ) -> Result<BTreeMap<PipelineKey, vk::Pipeline>, VulkanSmokeRendererError> { let mut pipelines = BTreeMap::new(); - for range in draw_ranges { - let key = range.pipeline_key(); + for state in base_pipeline_states + .iter() + .copied() + .flat_map(super::smoke::pipeline_variants) + { + let key = PipelineKey::from(state); if pipelines.contains_key(&key) { continue; } let pipeline = match create_graphics_pipeline( device, + pipeline_cache, render_pass, pipeline_layout, extent, - range.pipeline_state, + state, ) { Ok(pipeline) => pipeline, Err(error) => { @@ -964,6 +997,7 @@ fn create_graphics_pipeline_cache( fn create_graphics_pipeline( device: &VulkanLogicalDeviceProbe, + pipeline_cache: vk::PipelineCache, render_pass: vk::RenderPass, pipeline_layout: vk::PipelineLayout, extent: (u32, u32), @@ -1033,14 +1067,7 @@ fn create_graphics_pipeline( let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::default() .topology(vk::PrimitiveTopology::TRIANGLE_LIST) .primitive_restart_enable(false); - let viewports = [vk::Viewport { - x: 0.0, - y: 0.0, - width: extent_component_to_f32(extent.0), - height: extent_component_to_f32(extent.1), - min_depth: 0.0, - max_depth: 1.0, - }]; + let viewports = [draw_viewport(extent, 0.0)]; let scissors = [vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: vk::Extent2D { @@ -1051,6 +1078,8 @@ fn create_graphics_pipeline( let viewport_state = vk::PipelineViewportStateCreateInfo::default() .viewports(&viewports) .scissors(&scissors); + let (depth_bias_enable, depth_bias_constant_factor, depth_bias_slope_factor) = + depth_bias_parameters(state.depth_bias); let rasterization_state = vk::PipelineRasterizationStateCreateInfo::default() .depth_clamp_enable(false) .rasterizer_discard_enable(false) @@ -1062,7 +1091,10 @@ fn create_graphics_pipeline( LegacyCullMode::FrontFace => vk::CullModeFlags::FRONT, }) .front_face(vk::FrontFace::CLOCKWISE) - .depth_bias_enable(false); + .depth_bias_enable(depth_bias_enable) + .depth_bias_constant_factor(depth_bias_constant_factor) + .depth_bias_slope_factor(depth_bias_slope_factor) + .depth_bias_clamp(0.0); let multisample_state = vk::PipelineMultisampleStateCreateInfo::default() .sample_shading_enable(false) .rasterization_samples(vk::SampleCountFlags::TYPE_1); @@ -1105,6 +1137,9 @@ fn create_graphics_pipeline( let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default() .logic_op_enable(false) .attachments(&color_blend_attachments); + let dynamic_states = [vk::DynamicState::VIEWPORT]; + let dynamic_state = + vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); let create_info = vk::GraphicsPipelineCreateInfo::default() .stages(&shader_stages) .vertex_input_state(&vertex_input_state) @@ -1114,6 +1149,7 @@ fn create_graphics_pipeline( .multisample_state(&multisample_state) .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) + .dynamic_state(&dynamic_state) .layout(pipeline_layout) .render_pass(render_pass) .subpass(0); @@ -1122,7 +1158,7 @@ fn create_graphics_pipeline( let pipeline_result = unsafe { device .device() - .create_graphics_pipelines(vk::PipelineCache::null(), &create_infos, None) + .create_graphics_pipelines(pipeline_cache, &create_infos, None) }; // SAFETY: The shader modules were created above on this live logical device and are no longer needed after pipeline creation. unsafe { @@ -1328,3 +1364,52 @@ fn destroy_partial_swapchain_resources( } } } + +#[cfg(test)] +mod tests { + use super::{depth_bias_parameters, draw_viewport}; + use fparkan_render::LegacyDepthBias; + + #[test] + fn projected_decal_bias_parameters_use_empirical_vulkan_factors() { + assert_eq!( + depth_bias_parameters(LegacyDepthBias::Disabled), + (false, 0.0, 0.0) + ); + assert_eq!( + depth_bias_parameters(LegacyDepthBias::ProjectedDecal), + (true, -1.0, 0.0) + ); + } + + #[test] + fn world_viewport_depth_keeps_distant_geometry_in_front_of_screen_sun() { + let projection = fparkan_render::LegacyD3d7Projection { + viewport: [0, 0, 1280, 1024], + near_plane: 0.5, + far_plane: 700.0, + field_of_view_radians: 1.04, + } + .try_direct3d7_projection_row_major() + .expect("native projection"); + for mode in [0.0, 1.0, 2.0, 3.0, 16.0, 17.0] { + let viewport = draw_viewport((1280, 1024), mode); + assert_eq!([viewport.width, viewport.height], [1280.0, 1024.0]); + assert_eq!([viewport.min_depth, viewport.max_depth], [0.1, 0.99]); + for distance in [0.5, 100.0, 350.0, 600.0, 700.0] { + let clip_z = distance * projection[10] + projection[14]; + let clip_w = distance * projection[11]; + let depth = viewport.min_depth + + (viewport.max_depth - viewport.min_depth) * (clip_z / clip_w); + assert!( + depth < 0.999, + "mode {mode}, distance {distance}, depth {depth}" + ); + } + } + for mode in [4.0, 5.0, 6.0, 7.0, 9.0] { + let viewport = draw_viewport((640, 480), mode); + assert_eq!([viewport.min_depth, viewport.max_depth], [0.0, 1.0]); + } + } +} |
