aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs
diff options
context:
space:
mode:
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs')
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs163
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]);
+ }
+ }
+}