aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src/ffi
diff options
context:
space:
mode:
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi')
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs105
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs3
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs70
3 files changed, 119 insertions, 59 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index 2140cab..1b50914 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -364,6 +364,7 @@ impl VulkanSmokeRenderer {
device: Some(device),
swapchain: Some(swapchain),
command_pool,
+ pipeline_cache: vk::PipelineCache::null(),
swapchain_resources: None,
vertex_buffer: Some(vertex_buffer),
index_buffer: Some(index_buffer),
@@ -429,8 +430,21 @@ impl VulkanSmokeRenderer {
mesh_full_upload_bytes: 0,
initial_texture_image_count,
texture_detach_count: 0,
+ pipeline_creation_count: 0,
},
};
+ // SAFETY: This cache is owned by the renderer and only used from its
+ // exclusive methods; it outlives every pipeline creation and swapchain.
+ renderer.pipeline_cache = unsafe {
+ renderer
+ .device_ref()?
+ .device()
+ .create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None)
+ }
+ .map_err(|result| VulkanSmokeRendererError::VulkanOperation {
+ context: "vkCreatePipelineCache",
+ result,
+ })?;
renderer.rebuild_swapchain_resources(false)?;
let device_ref = renderer.device_ref()?;
let swapchain_ref = renderer.swapchain_ref()?;
@@ -465,6 +479,7 @@ impl VulkanSmokeRenderer {
mesh_full_upload_bytes: 0,
initial_texture_image_count,
texture_detach_count: 0,
+ pipeline_creation_count: renderer.report.pipeline_creation_count,
};
Ok(renderer)
}
@@ -962,11 +977,7 @@ impl VulkanSmokeRenderer {
},
)?;
self.draw_range_base_pipeline_states[range_index] = next_base;
- let pipeline_changed = range.pipeline_state != next_state;
range.pipeline_state = next_state;
- if pipeline_changed {
- self.rebuild_pipeline_resources()?;
- }
Ok(())
}
@@ -1035,7 +1046,6 @@ impl VulkanSmokeRenderer {
},
)?;
let next_state = apply_world_transparency(base_state, transparent);
- let pipeline_changed = range.pipeline_state != next_state;
range.pipeline_state = next_state;
// Calling this setter opts the range into the world queue. Fixed
// screen/sky/weather ranges remain `None` because their pass order is
@@ -1045,9 +1055,6 @@ impl VulkanSmokeRenderer {
Some(transparent),
&mut self.draw_order_dirty,
);
- if pipeline_changed {
- self.rebuild_pipeline_resources()?;
- }
Ok(())
}
@@ -1673,11 +1680,12 @@ impl VulkanSmokeRenderer {
device,
swapchain,
self.command_pool,
+ self.pipeline_cache,
self.vertex_buffer_ref()?,
self.index_buffer_ref()?,
self.textures_ref()?,
&self.texture_slot_indices,
- &self.draw_ranges,
+ &self.draw_range_base_pipeline_states,
self.depth_request,
reuse_command_pool,
)?,
@@ -1696,32 +1704,14 @@ impl VulkanSmokeRenderer {
self.report.swapchain_image_count = swapchain_image_count;
self.report.swapchain_image_format = self.swapchain_ref()?.report.plan.format.format;
self.report.swapchain_image_usage = swapchain_image_usage;
+ self.report.pipeline_creation_count = self
+ .report
+ .pipeline_creation_count
+ .saturating_add(resources.pipelines.len() as u64);
self.swapchain_resources = Some(resources);
Ok(())
}
- /// Rebuilds only the swapchain-owned pipeline bundle after a structural
- /// range-state change. Draw-range setters can be called while a renderer
- /// is live, so all prior submissions are completed before handles are
- /// destroyed and recreated.
- fn rebuild_pipeline_resources(&mut self) -> Result<(), VulkanSmokeRendererError> {
- if self.swapchain_resources.is_none() {
- return Ok(());
- }
- let device = self.device_ref()?;
- // SAFETY: The renderer owns every submission on this device. Waiting
- // here makes the old pipelines, framebuffers, and command buffers
- // quiescent before their destruction.
- unsafe { device.device().device_wait_idle() }.map_err(|result| {
- VulkanSmokeRendererError::VulkanOperation {
- context: "vkDeviceWaitIdle(draw range pipeline update)",
- result,
- }
- })?;
- self.destroy_swapchain_resources();
- self.rebuild_swapchain_resources(true)
- }
-
/// Recomputes the stable command-recording order. Without explicit
/// backgrounds, opaque world ranges followed by the sorted transparent
/// world ranges replace the existing world slots. With explicit
@@ -2071,6 +2061,15 @@ impl VulkanSmokeRenderer {
fn destroy_device_owned_resources(&mut self) {
self.destroy_swapchain_resources();
if let Some(device) = self.device.as_ref() {
+ if self.pipeline_cache != vk::PipelineCache::null() {
+ // SAFETY: All pipeline creation has finished on this device.
+ unsafe {
+ device
+ .device()
+ .destroy_pipeline_cache(self.pipeline_cache, None)
+ };
+ self.pipeline_cache = vk::PipelineCache::null();
+ }
for texture in self.textures.drain(..) {
destroy_allocated_image(device, &texture);
}
@@ -2429,6 +2428,18 @@ fn draw_material_alphas(
]
}
+pub(super) fn pipeline_variants(original: LegacyPipelineState) -> [LegacyPipelineState; 12] {
+ std::array::from_fn(|index| match index {
+ 0 => original,
+ 1 => apply_world_transparency(original, true),
+ _ => {
+ world_material_pipeline_states(original, ((index - 2) / 2) as u8, Some(index % 2 != 0))
+ .expect("categories 0..=4 are valid")
+ .1
+ }
+ })
+}
+
fn world_material_pipeline_states(
original: LegacyPipelineState,
category: u8,
@@ -2597,7 +2608,7 @@ mod tests {
use super::{
apply_geometry_range_updates, apply_world_transparency, completed_readback_buffer_index,
deduplicate_texture_sources, draw_material_alphas, geometry_upload_range,
- mark_draw_order_dirty_on_activity_change, set_and_mark_dirty_if_changed,
+ mark_draw_order_dirty_on_activity_change, pipeline_variants, set_and_mark_dirty_if_changed,
sorted_world_draw_order, take_runtime_children_with_validation_snapshot,
take_runtime_owners_in_dependency_order, validate_geometry_upload,
world_material_pipeline_states, RollbackOnDrop,
@@ -2758,6 +2769,36 @@ mod tests {
}
#[test]
+ fn pipeline_variants_keep_authored_alpha_and_prepare_both_depth_policies() {
+ let original = LegacyPipelineState {
+ alpha_test: true,
+ depth: LegacyDepthMode::TestWrite,
+ cull: LegacyCullMode::FrontFace,
+ depth_bias: LegacyDepthBias::ProjectedDecal,
+ ..LegacyPipelineState::default()
+ };
+ let states = pipeline_variants(original);
+ let keys: std::collections::BTreeSet<_> = states
+ .iter()
+ .map(|state| fparkan_render::PipelineKey::from(*state))
+ .collect();
+ assert_eq!(keys.len(), 12);
+ assert_eq!(states[0], original);
+ assert_eq!(states[1], apply_world_transparency(original, true));
+ for state in states {
+ assert_eq!(state.cull, original.cull);
+ assert_eq!(state.depth_bias, original.depth_bias);
+ }
+ let disabled = pipeline_variants(LegacyPipelineState {
+ depth: LegacyDepthMode::Disabled,
+ ..original
+ });
+ assert!(disabled
+ .iter()
+ .all(|state| state.depth == LegacyDepthMode::Disabled));
+ }
+
+ #[test]
fn texture_deduplication_compares_pixels_extents_and_authored_mips() {
use super::super::{VulkanStaticTexture, VulkanStaticTextureMip};
let base = VulkanStaticTexture {
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
index 5bc5d8c..5d6f12f 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -1343,6 +1343,8 @@ pub struct VulkanSmokeRendererReport {
pub initial_texture_image_count: u64,
/// Shared images detached for independent material-slot updates.
pub texture_detach_count: u64,
+ /// Graphics pipelines created across swapchain lifetimes.
+ pub pipeline_creation_count: u64,
}
/// Measured validation counters from the live smoke loop.
@@ -1485,6 +1487,7 @@ pub struct VulkanSmokeRenderer {
pub(super) device: Option<VulkanLogicalDeviceProbe>,
pub(super) swapchain: Option<VulkanSwapchainProbe>,
pub(super) command_pool: vk::CommandPool,
+ pub(super) pipeline_cache: vk::PipelineCache,
pub(super) swapchain_resources: Option<VulkanSwapchainResources>,
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs
index 973e8b1..a44f79d 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs
@@ -174,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> {
@@ -281,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
@@ -449,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,
@@ -510,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,
@@ -946,23 +955,29 @@ fn depth_bias_parameters(depth_bias: LegacyDepthBias) -> (bool, f32, f32) {
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) => {
@@ -982,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),
@@ -1142,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 {