diff options
| author | Valentin Popov <valentin@popov.link> | 2026-10-30 17:28:45 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-10-30 17:28:45 +0300 |
| commit | 2e5058078c586e2dd0dcf3bbcf149223e5f0daeb (patch) | |
| tree | 878cccd811aa54fa2046928b45e6cc9c435d13bc /adapters/fparkan-render-vulkan/src/ffi | |
| parent | bce6e3b2639d8771e50e2f907cdfa110671717b1 (diff) | |
| download | fparkan-2e5058078c586e2dd0dcf3bbcf149223e5f0daeb.tar.xz fparkan-2e5058078c586e2dd0dcf3bbcf149223e5f0daeb.zip | |
fix(render): preserve native world viewport depth range
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi')
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/smoke.rs | 12 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs | 62 |
2 files changed, 64 insertions, 10 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs index a2481f7..90bce14 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs @@ -1808,6 +1808,7 @@ impl VulkanSmokeRenderer { 0, vk::IndexType::UINT32, ); + let mut viewport_depth = None; for &range_index in &self.draw_order { let range = self.draw_ranges.get(range_index).ok_or( VulkanSmokeRendererError::InvariantViolation { @@ -1929,6 +1930,17 @@ impl VulkanSmokeRenderer { } else { *material_mode }; + let viewport = super::swapchain_resources::draw_viewport( + swapchain.report.plan.extent, + material_mode, + ); + let next_viewport_depth = [viewport.min_depth, viewport.max_depth]; + if viewport_depth != Some(next_viewport_depth) { + device + .device() + .cmd_set_viewport(command_buffer, 0, &[viewport]); + viewport_depth = Some(next_viewport_depth); + } let material_mode = [material_mode.to_ne_bytes()]; device.device().cmd_push_constants( command_buffer, diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs index 89ca43a..05ce927 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs @@ -922,8 +922,17 @@ 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 create_graphics_pipeline_cache( @@ -1033,14 +1042,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 { @@ -1105,6 +1107,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 +1119,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); @@ -1328,3 +1334,39 @@ fn destroy_partial_swapchain_resources( } } } + +#[cfg(test)] +mod tests { + use super::draw_viewport; + + #[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]); + } + } +} |
