perf(render): prewarm pipeline variants with a Vulkan cache
Docs Deploy / Build and Deploy MkDocs (push) Successful in 35s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 35s
This commit is contained in:
5 files changed
+118
-44
No files matched your search
@@ -364,6 +364,7 @@ impl VulkanSmokeRenderer {
|
|||||||
device: Some(device),
|
device: Some(device),
|
||||||
swapchain: Some(swapchain),
|
swapchain: Some(swapchain),
|
||||||
command_pool,
|
command_pool,
|
||||||
|
pipeline_cache: vk::PipelineCache::null(),
|
||||||
swapchain_resources: None,
|
swapchain_resources: None,
|
||||||
vertex_buffer: Some(vertex_buffer),
|
vertex_buffer: Some(vertex_buffer),
|
||||||
index_buffer: Some(index_buffer),
|
index_buffer: Some(index_buffer),
|
||||||
@@ -429,8 +430,21 @@ impl VulkanSmokeRenderer {
|
|||||||
mesh_full_upload_bytes: 0,
|
mesh_full_upload_bytes: 0,
|
||||||
initial_texture_image_count,
|
initial_texture_image_count,
|
||||||
texture_detach_count: 0,
|
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)?;
|
renderer.rebuild_swapchain_resources(false)?;
|
||||||
let device_ref = renderer.device_ref()?;
|
let device_ref = renderer.device_ref()?;
|
||||||
let swapchain_ref = renderer.swapchain_ref()?;
|
let swapchain_ref = renderer.swapchain_ref()?;
|
||||||
@@ -465,6 +479,7 @@ impl VulkanSmokeRenderer {
|
|||||||
mesh_full_upload_bytes: 0,
|
mesh_full_upload_bytes: 0,
|
||||||
initial_texture_image_count,
|
initial_texture_image_count,
|
||||||
texture_detach_count: 0,
|
texture_detach_count: 0,
|
||||||
|
pipeline_creation_count: renderer.report.pipeline_creation_count,
|
||||||
};
|
};
|
||||||
Ok(renderer)
|
Ok(renderer)
|
||||||
}
|
}
|
||||||
@@ -962,11 +977,7 @@ impl VulkanSmokeRenderer {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
self.draw_range_base_pipeline_states[range_index] = next_base;
|
self.draw_range_base_pipeline_states[range_index] = next_base;
|
||||||
let pipeline_changed = range.pipeline_state != next_state;
|
|
||||||
range.pipeline_state = next_state;
|
range.pipeline_state = next_state;
|
||||||
if pipeline_changed {
|
|
||||||
self.rebuild_pipeline_resources()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,7 +1046,6 @@ impl VulkanSmokeRenderer {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
let next_state = apply_world_transparency(base_state, transparent);
|
let next_state = apply_world_transparency(base_state, transparent);
|
||||||
let pipeline_changed = range.pipeline_state != next_state;
|
|
||||||
range.pipeline_state = next_state;
|
range.pipeline_state = next_state;
|
||||||
// Calling this setter opts the range into the world queue. Fixed
|
// Calling this setter opts the range into the world queue. Fixed
|
||||||
// screen/sky/weather ranges remain `None` because their pass order is
|
// screen/sky/weather ranges remain `None` because their pass order is
|
||||||
@@ -1045,9 +1055,6 @@ impl VulkanSmokeRenderer {
|
|||||||
Some(transparent),
|
Some(transparent),
|
||||||
&mut self.draw_order_dirty,
|
&mut self.draw_order_dirty,
|
||||||
);
|
);
|
||||||
if pipeline_changed {
|
|
||||||
self.rebuild_pipeline_resources()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1673,11 +1680,12 @@ impl VulkanSmokeRenderer {
|
|||||||
device,
|
device,
|
||||||
swapchain,
|
swapchain,
|
||||||
self.command_pool,
|
self.command_pool,
|
||||||
|
self.pipeline_cache,
|
||||||
self.vertex_buffer_ref()?,
|
self.vertex_buffer_ref()?,
|
||||||
self.index_buffer_ref()?,
|
self.index_buffer_ref()?,
|
||||||
self.textures_ref()?,
|
self.textures_ref()?,
|
||||||
&self.texture_slot_indices,
|
&self.texture_slot_indices,
|
||||||
&self.draw_ranges,
|
&self.draw_range_base_pipeline_states,
|
||||||
self.depth_request,
|
self.depth_request,
|
||||||
reuse_command_pool,
|
reuse_command_pool,
|
||||||
)?,
|
)?,
|
||||||
@@ -1696,32 +1704,14 @@ impl VulkanSmokeRenderer {
|
|||||||
self.report.swapchain_image_count = swapchain_image_count;
|
self.report.swapchain_image_count = swapchain_image_count;
|
||||||
self.report.swapchain_image_format = self.swapchain_ref()?.report.plan.format.format;
|
self.report.swapchain_image_format = self.swapchain_ref()?.report.plan.format.format;
|
||||||
self.report.swapchain_image_usage = swapchain_image_usage;
|
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);
|
self.swapchain_resources = Some(resources);
|
||||||
Ok(())
|
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
|
/// Recomputes the stable command-recording order. Without explicit
|
||||||
/// backgrounds, opaque world ranges followed by the sorted transparent
|
/// backgrounds, opaque world ranges followed by the sorted transparent
|
||||||
/// world ranges replace the existing world slots. With explicit
|
/// world ranges replace the existing world slots. With explicit
|
||||||
@@ -2071,6 +2061,15 @@ impl VulkanSmokeRenderer {
|
|||||||
fn destroy_device_owned_resources(&mut self) {
|
fn destroy_device_owned_resources(&mut self) {
|
||||||
self.destroy_swapchain_resources();
|
self.destroy_swapchain_resources();
|
||||||
if let Some(device) = self.device.as_ref() {
|
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(..) {
|
for texture in self.textures.drain(..) {
|
||||||
destroy_allocated_image(device, &texture);
|
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(
|
fn world_material_pipeline_states(
|
||||||
original: LegacyPipelineState,
|
original: LegacyPipelineState,
|
||||||
category: u8,
|
category: u8,
|
||||||
@@ -2597,7 +2608,7 @@ mod tests {
|
|||||||
use super::{
|
use super::{
|
||||||
apply_geometry_range_updates, apply_world_transparency, completed_readback_buffer_index,
|
apply_geometry_range_updates, apply_world_transparency, completed_readback_buffer_index,
|
||||||
deduplicate_texture_sources, draw_material_alphas, geometry_upload_range,
|
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,
|
sorted_world_draw_order, take_runtime_children_with_validation_snapshot,
|
||||||
take_runtime_owners_in_dependency_order, validate_geometry_upload,
|
take_runtime_owners_in_dependency_order, validate_geometry_upload,
|
||||||
world_material_pipeline_states, RollbackOnDrop,
|
world_material_pipeline_states, RollbackOnDrop,
|
||||||
@@ -2757,6 +2768,36 @@ mod tests {
|
|||||||
assert_eq!(ranges[1].index_count, 3);
|
assert_eq!(ranges[1].index_count, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn texture_deduplication_compares_pixels_extents_and_authored_mips() {
|
fn texture_deduplication_compares_pixels_extents_and_authored_mips() {
|
||||||
use super::super::{VulkanStaticTexture, VulkanStaticTextureMip};
|
use super::super::{VulkanStaticTexture, VulkanStaticTextureMip};
|
||||||
|
|||||||
@@ -1343,6 +1343,8 @@ pub struct VulkanSmokeRendererReport {
|
|||||||
pub initial_texture_image_count: u64,
|
pub initial_texture_image_count: u64,
|
||||||
/// Shared images detached for independent material-slot updates.
|
/// Shared images detached for independent material-slot updates.
|
||||||
pub texture_detach_count: u64,
|
pub texture_detach_count: u64,
|
||||||
|
/// Graphics pipelines created across swapchain lifetimes.
|
||||||
|
pub pipeline_creation_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Measured validation counters from the live smoke loop.
|
/// Measured validation counters from the live smoke loop.
|
||||||
@@ -1485,6 +1487,7 @@ pub struct VulkanSmokeRenderer {
|
|||||||
pub(super) device: Option<VulkanLogicalDeviceProbe>,
|
pub(super) device: Option<VulkanLogicalDeviceProbe>,
|
||||||
pub(super) swapchain: Option<VulkanSwapchainProbe>,
|
pub(super) swapchain: Option<VulkanSwapchainProbe>,
|
||||||
pub(super) command_pool: vk::CommandPool,
|
pub(super) command_pool: vk::CommandPool,
|
||||||
|
pub(super) pipeline_cache: vk::PipelineCache,
|
||||||
pub(super) swapchain_resources: Option<VulkanSwapchainResources>,
|
pub(super) swapchain_resources: Option<VulkanSwapchainResources>,
|
||||||
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
|
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
|
||||||
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
|
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
|
||||||
|
|||||||
@@ -174,11 +174,12 @@ pub(super) fn create_swapchain_resources(
|
|||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
swapchain: &VulkanSwapchainProbe,
|
swapchain: &VulkanSwapchainProbe,
|
||||||
command_pool: vk::CommandPool,
|
command_pool: vk::CommandPool,
|
||||||
|
pipeline_cache: vk::PipelineCache,
|
||||||
vertex_buffer: &VulkanAllocatedBuffer,
|
vertex_buffer: &VulkanAllocatedBuffer,
|
||||||
index_buffer: &VulkanAllocatedBuffer,
|
index_buffer: &VulkanAllocatedBuffer,
|
||||||
textures: &[VulkanAllocatedImage],
|
textures: &[VulkanAllocatedImage],
|
||||||
texture_slot_indices: &[usize],
|
texture_slot_indices: &[usize],
|
||||||
draw_ranges: &[super::VulkanStaticDrawRange],
|
base_pipeline_states: &[LegacyPipelineState],
|
||||||
depth_request: DepthStencilSupport,
|
depth_request: DepthStencilSupport,
|
||||||
reuse_command_pool: bool,
|
reuse_command_pool: bool,
|
||||||
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
|
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
|
||||||
@@ -281,11 +282,12 @@ pub(super) fn create_swapchain_resources(
|
|||||||
) = match create_swapchain_pipeline_bundle(
|
) = match create_swapchain_pipeline_bundle(
|
||||||
instance,
|
instance,
|
||||||
device,
|
device,
|
||||||
|
pipeline_cache,
|
||||||
swapchain.report.plan.format.format,
|
swapchain.report.plan.format.format,
|
||||||
swapchain.report.plan.extent,
|
swapchain.report.plan.extent,
|
||||||
textures,
|
textures,
|
||||||
texture_slot_indices,
|
texture_slot_indices,
|
||||||
draw_ranges,
|
base_pipeline_states,
|
||||||
depth_request,
|
depth_request,
|
||||||
partial
|
partial
|
||||||
.frame_uniform_buffer
|
.frame_uniform_buffer
|
||||||
@@ -449,11 +451,12 @@ fn create_swapchain_image_views(
|
|||||||
fn create_swapchain_pipeline_bundle(
|
fn create_swapchain_pipeline_bundle(
|
||||||
instance: &VulkanInstanceProbe,
|
instance: &VulkanInstanceProbe,
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
|
pipeline_cache: vk::PipelineCache,
|
||||||
format: i32,
|
format: i32,
|
||||||
extent: (u32, u32),
|
extent: (u32, u32),
|
||||||
textures: &[VulkanAllocatedImage],
|
textures: &[VulkanAllocatedImage],
|
||||||
texture_slot_indices: &[usize],
|
texture_slot_indices: &[usize],
|
||||||
draw_ranges: &[super::VulkanStaticDrawRange],
|
base_pipeline_states: &[LegacyPipelineState],
|
||||||
depth_request: DepthStencilSupport,
|
depth_request: DepthStencilSupport,
|
||||||
frame_uniform_buffer: &VulkanAllocatedBuffer,
|
frame_uniform_buffer: &VulkanAllocatedBuffer,
|
||||||
material_specular_buffer: &VulkanAllocatedBuffer,
|
material_specular_buffer: &VulkanAllocatedBuffer,
|
||||||
@@ -510,8 +513,14 @@ fn create_swapchain_pipeline_bundle(
|
|||||||
}
|
}
|
||||||
destroy_depth_attachment(device, &depth_attachment);
|
destroy_depth_attachment(device, &depth_attachment);
|
||||||
})?;
|
})?;
|
||||||
let pipelines =
|
let pipelines = create_graphics_pipeline_cache(
|
||||||
create_graphics_pipeline_cache(device, render_pass, pipeline_layout, extent, draw_ranges)
|
device,
|
||||||
|
pipeline_cache,
|
||||||
|
render_pass,
|
||||||
|
pipeline_layout,
|
||||||
|
extent,
|
||||||
|
base_pipeline_states,
|
||||||
|
)
|
||||||
.inspect_err(|_| {
|
.inspect_err(|_| {
|
||||||
// SAFETY: These objects were created above on this live logical device and are destroyed on setup failure.
|
// SAFETY: These objects were created above on this live logical device and are destroyed on setup failure.
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -946,23 +955,29 @@ fn depth_bias_parameters(depth_bias: LegacyDepthBias) -> (bool, f32, f32) {
|
|||||||
|
|
||||||
fn create_graphics_pipeline_cache(
|
fn create_graphics_pipeline_cache(
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
|
pipeline_cache: vk::PipelineCache,
|
||||||
render_pass: vk::RenderPass,
|
render_pass: vk::RenderPass,
|
||||||
pipeline_layout: vk::PipelineLayout,
|
pipeline_layout: vk::PipelineLayout,
|
||||||
extent: (u32, u32),
|
extent: (u32, u32),
|
||||||
draw_ranges: &[super::VulkanStaticDrawRange],
|
base_pipeline_states: &[LegacyPipelineState],
|
||||||
) -> Result<BTreeMap<PipelineKey, vk::Pipeline>, VulkanSmokeRendererError> {
|
) -> Result<BTreeMap<PipelineKey, vk::Pipeline>, VulkanSmokeRendererError> {
|
||||||
let mut pipelines = BTreeMap::new();
|
let mut pipelines = BTreeMap::new();
|
||||||
for range in draw_ranges {
|
for state in base_pipeline_states
|
||||||
let key = range.pipeline_key();
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.flat_map(super::smoke::pipeline_variants)
|
||||||
|
{
|
||||||
|
let key = PipelineKey::from(state);
|
||||||
if pipelines.contains_key(&key) {
|
if pipelines.contains_key(&key) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let pipeline = match create_graphics_pipeline(
|
let pipeline = match create_graphics_pipeline(
|
||||||
device,
|
device,
|
||||||
|
pipeline_cache,
|
||||||
render_pass,
|
render_pass,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
extent,
|
extent,
|
||||||
range.pipeline_state,
|
state,
|
||||||
) {
|
) {
|
||||||
Ok(pipeline) => pipeline,
|
Ok(pipeline) => pipeline,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -982,6 +997,7 @@ fn create_graphics_pipeline_cache(
|
|||||||
|
|
||||||
fn create_graphics_pipeline(
|
fn create_graphics_pipeline(
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
|
pipeline_cache: vk::PipelineCache,
|
||||||
render_pass: vk::RenderPass,
|
render_pass: vk::RenderPass,
|
||||||
pipeline_layout: vk::PipelineLayout,
|
pipeline_layout: vk::PipelineLayout,
|
||||||
extent: (u32, u32),
|
extent: (u32, u32),
|
||||||
@@ -1142,7 +1158,7 @@ fn create_graphics_pipeline(
|
|||||||
let pipeline_result = unsafe {
|
let pipeline_result = unsafe {
|
||||||
device
|
device
|
||||||
.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.
|
// SAFETY: The shader modules were created above on this live logical device and are no longer needed after pipeline creation.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
@@ -10623,6 +10623,10 @@ impl StaticVulkanApp {
|
|||||||
report.renderer_report.initial_texture_image_count,
|
report.renderer_report.initial_texture_image_count,
|
||||||
report.renderer_report.texture_detach_count,
|
report.renderer_report.texture_detach_count,
|
||||||
);
|
);
|
||||||
|
eprintln!(
|
||||||
|
"render_pipeline_cache pipelines_created={}",
|
||||||
|
report.renderer_report.pipeline_creation_count
|
||||||
|
);
|
||||||
self.output = Some(format!(
|
self.output = Some(format!(
|
||||||
"rendered mission {}: {} frames, {} objects, {} mesh components, {} terrain components, camera={}, captured_viewport_crop={}, control_clock_ms={}, control_clock_mode={}, materials={}, environment_primitives={}, shadow_casters={}, shadow_indices={}, swapchain_recreates={}, validation={}, swapchain_format={}, readback_format={}, readback={}, readback_sequence={}, startup_ms={{mission_assets:{:.1},terrain_materials:{:.1},sky:{:.1},gpu_initialization:{:.1}}}",
|
"rendered mission {}: {} frames, {} objects, {} mesh components, {} terrain components, camera={}, captured_viewport_crop={}, control_clock_ms={}, control_clock_mode={}, materials={}, environment_primitives={}, shadow_casters={}, shadow_indices={}, swapchain_recreates={}, validation={}, swapchain_format={}, readback_format={}, readback={}, readback_sequence={}, startup_ms={{mission_assets:{:.1},terrain_materials:{:.1},sky:{:.1},gpu_initialization:{:.1}}}",
|
||||||
self.mission,
|
self.mission,
|
||||||
|
|||||||
+12
-2
@@ -1144,6 +1144,16 @@ lightmap state передаются в `VulkanStaticMaterial`, а shader пос
|
|||||||
получается ключ Vulkan pipeline. Alpha reference передаётся отдельно через
|
получается ключ Vulkan pipeline. Alpha reference передаётся отдельно через
|
||||||
push constant; он не требует нового pipeline.
|
push constant; он не требует нового pipeline.
|
||||||
|
|
||||||
|
Набор pipelines создаётся до первого draw. Для каждого исходного состояния
|
||||||
|
готовятся его авторский вариант, прозрачный вариант с сохранённым alpha-test
|
||||||
|
и пять категорий MAT0 с обеими политиками глубины. Cull и decal bias остаются
|
||||||
|
исходными; одинаковые ключи объединяются. Смена категории материала или
|
||||||
|
прозрачности диапазона выбирает готовый ключ, сохраняя descriptors, buffers и
|
||||||
|
framebuffers. Vulkan `VkPipelineCache` принадлежит renderer, используется всеми
|
||||||
|
созданиями pipelines и переживает пересоздание swapchain. На диск он пока не
|
||||||
|
сохраняется. Счётчик `render_pipeline_cache` показывает число созданных
|
||||||
|
pipelines, включая новые наборы после resize.
|
||||||
|
|
||||||
Рабочий путь `fparkan-game` загружает terrain и все выбранные mission roots
|
Рабочий путь `fparkan-game` загружает terrain и все выбранные mission roots
|
||||||
(по умолчанию весь список объектов), разрешая локальные material slots через
|
(по умолчанию весь список объектов), разрешая локальные material slots через
|
||||||
WEAR и MAT0 до объединения геометрии. Индексы GPU имеют тип `u32`: вся карта
|
WEAR и MAT0 до объединения геометрии. Индексы GPU имеют тип `u32`: вся карта
|
||||||
@@ -1171,8 +1181,8 @@ FX и окружение обновляют следующие вершины,
|
|||||||
|
|
||||||
Перед записью общих ресурсов renderer ждёт fences всех отправленных кадров.
|
Перед записью общих ресурсов renderer ждёт fences всех отправленных кадров.
|
||||||
Одного fence текущего кадрового слота недостаточно: другой кадр ещё может
|
Одного fence текущего кадрового слота недостаточно: другой кадр ещё может
|
||||||
читать ту же память. Ожидание всего устройства пока остаётся на пересоздании
|
читать ту же память. Ожидание всего устройства остаётся на пересоздании
|
||||||
swapchain и набора pipelines, диагностическом readback и завершении работы. Загрузка изменённых
|
swapchain, диагностическом readback и завершении работы. Загрузка изменённых
|
||||||
текстур пока использует отдельную синхронную transfer-команду. Строка
|
текстур пока использует отдельную синхронную transfer-команду. Строка
|
||||||
`render_geometry_upload` показывает записанные vertex/index bytes и объём,
|
`render_geometry_upload` показывает записанные vertex/index bytes и объём,
|
||||||
который потребовался бы при полной перезаливке, без начальной загрузки.
|
который потребовался бы при полной перезаливке, без начальной загрузки.
|
||||||
|
|||||||
Reference in new issue
Block a user