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.rs176
1 files changed, 140 insertions, 36 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs
index 05ce927..af20dfa 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;
@@ -17,6 +18,7 @@ use super::{
};
pub(super) struct VulkanSwapchainResources {
+ pub(super) reflection: super::reflection::ReflectionResources,
pub(super) image_views: Vec<vk::ImageView>,
pub(super) images: Vec<vk::Image>,
pub(super) readback_buffers: Vec<VulkanAllocatedBuffer>,
@@ -145,6 +147,7 @@ pub(super) fn ensure_point_light_capacity(
}
struct PartialSwapchainResources {
+ reflection: Option<super::reflection::ReflectionResources>,
image_views: Vec<vk::ImageView>,
readback_buffers: Vec<VulkanAllocatedBuffer>,
depth_attachments: Vec<VulkanDepthAttachment>,
@@ -173,11 +176,13 @@ 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],
+ reflection_enabled: bool,
depth_request: DepthStencilSupport,
reuse_command_pool: bool,
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
@@ -192,6 +197,7 @@ pub(super) fn create_swapchain_resources(
result: error,
})?;
let mut partial = PartialSwapchainResources {
+ reflection: None,
image_views: create_swapchain_image_views(
device,
&images,
@@ -243,6 +249,18 @@ pub(super) fn create_swapchain_resources(
};
partial.frame_uniform_stride = frame_uniform_stride;
partial.frame_uniform_buffer = Some(frame_uniform_buffer);
+ partial.reflection = match super::reflection::ReflectionResources::new(
+ instance,
+ device,
+ command_pool,
+ reflection_enabled,
+ ) {
+ Ok(reflection) => Some(reflection),
+ Err(error) => {
+ destroy_partial_swapchain_resources(device, command_pool, partial);
+ return Err(error);
+ }
+ };
let material_count = texture_slot_indices.len() / 4;
let (material_specular_buffer, material_specular_stride) =
match create_material_specular_buffer(instance, device, material_count) {
@@ -280,11 +298,18 @@ 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,
+ partial
+ .reflection
+ .as_ref()
+ .expect("reflection descriptors exist")
+ .layout,
+ reflection_enabled,
depth_request,
partial
.frame_uniform_buffer
@@ -316,6 +341,24 @@ pub(super) fn create_swapchain_resources(
partial.descriptor_pool = Some(descriptor_pool);
partial.sampler = Some(sampler);
partial.pipelines = pipelines;
+ if reflection_enabled {
+ if let Err(error) = partial
+ .reflection
+ .as_mut()
+ .expect("reflection descriptors exist")
+ .create_target(
+ instance,
+ device,
+ swapchain.report.plan.extent,
+ swapchain.report.plan.format.format,
+ depth_request,
+ render_pass,
+ )
+ {
+ destroy_partial_swapchain_resources(device, command_pool, partial);
+ return Err(error);
+ }
+ }
for _ in 1..images.len() {
match create_depth_attachment(
instance,
@@ -391,6 +434,10 @@ pub(super) fn create_swapchain_resources(
});
}
Ok(VulkanSwapchainResources {
+ reflection: partial
+ .reflection
+ .take()
+ .expect("reflection descriptors exist"),
image_views: partial.image_views,
images,
readback_buffers: partial.readback_buffers,
@@ -448,11 +495,14 @@ 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],
+ reflection_layout: vk::DescriptorSetLayout,
+ reflection_enabled: bool,
depth_request: DepthStencilSupport,
frame_uniform_buffer: &VulkanAllocatedBuffer,
material_specular_buffer: &VulkanAllocatedBuffer,
@@ -494,8 +544,8 @@ fn create_swapchain_pipeline_bundle(
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(|_| {
+ let pipeline_layout = create_pipeline_layout(device, descriptor_set_layout, reflection_layout)
+ .inspect_err(|_| {
// SAFETY: The descriptor resources and render pass were created above and are rolled back on this device.
unsafe {
device.device().destroy_sampler(sampler, None);
@@ -509,25 +559,32 @@ 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,
+ reflection_enabled,
+ )
+ .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,
@@ -626,7 +683,7 @@ fn create_render_pass(
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::STORE)
.initial_layout(vk::ImageLayout::UNDEFINED)
- .final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
+ .final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let color_attachment_ref = vk::AttachmentReference::default()
.attachment(0)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
@@ -651,7 +708,8 @@ fn create_render_pass(
.src_subpass(vk::SUBPASS_EXTERNAL)
.dst_subpass(0)
.src_stage_mask(
- vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
+ vk::PipelineStageFlags::FRAGMENT_SHADER
+ | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
| vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
)
@@ -660,7 +718,11 @@ fn create_render_pass(
| vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
| vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
)
- .src_access_mask(vk::AccessFlags::empty())
+ .src_access_mask(
+ vk::AccessFlags::SHADER_READ
+ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE
+ | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
+ )
.dst_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
@@ -684,8 +746,9 @@ fn create_render_pass(
fn create_pipeline_layout(
device: &VulkanLogicalDeviceProbe,
descriptor_set_layout: vk::DescriptorSetLayout,
+ reflection_layout: vk::DescriptorSetLayout,
) -> Result<vk::PipelineLayout, VulkanSmokeRendererError> {
- let set_layouts = [descriptor_set_layout];
+ let set_layouts = [descriptor_set_layout, reflection_layout];
// Material data occupies one 128-byte push-constant allocation shared by
// both stages; the vertex stage also reads its specular pair from the
// dynamic storage buffer at descriptor binding 5. Camera data is supplied
@@ -935,25 +998,45 @@ pub(super) fn draw_viewport(extent: (u32, u32), material_mode: f32) -> vk::Viewp
}
}
+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],
+ reflection_enabled: bool,
) -> 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)
+ .flat_map(|state| {
+ std::iter::once(state).chain(
+ reflection_enabled.then_some(super::reflection::reflected_pipeline_state(state)),
+ )
+ })
+ {
+ 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) => {
@@ -973,6 +1056,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),
@@ -1053,6 +1137,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)
@@ -1064,7 +1150,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);
@@ -1128,7 +1217,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 {
@@ -1208,6 +1297,7 @@ pub(super) fn destroy_swapchain_resources(
command_pool: vk::CommandPool,
resources: VulkanSwapchainResources,
) {
+ drop(resources.reflection);
// SAFETY: All handles belong to this live logical device and are destroyed during renderer teardown/recreation.
unsafe {
if !resources.command_buffers.is_empty() {
@@ -1273,6 +1363,7 @@ fn destroy_partial_swapchain_resources(
command_pool: vk::CommandPool,
partial: PartialSwapchainResources,
) {
+ drop(partial.reflection);
// SAFETY: All handles in the partial bundle belong to this live logical device and are destroyed on setup failure.
unsafe {
if !partial.command_buffers.is_empty() {
@@ -1337,7 +1428,20 @@ fn destroy_partial_swapchain_resources(
#[cfg(test)]
mod tests {
- use super::draw_viewport;
+ 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() {