feat(vulkan): cache pipeline variants by state key
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::{VulkanStaticDrawRange, VulkanStaticMesh, VulkanStaticVertex};
|
use crate::{VulkanStaticDrawRange, VulkanStaticMesh, VulkanStaticVertex};
|
||||||
use fparkan_msh::ModelAsset;
|
use fparkan_msh::ModelAsset;
|
||||||
|
use fparkan_render::LegacyPipelineState;
|
||||||
|
|
||||||
/// Error returned when a validated MSH cannot enter the current static GPU path.
|
/// Error returned when a validated MSH cannot enter the current static GPU path.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
@@ -69,6 +70,7 @@ pub fn project_msh_to_static_mesh(
|
|||||||
first_index,
|
first_index,
|
||||||
index_count: u32::from(batch.index_count),
|
index_count: u32::from(batch.index_count),
|
||||||
material_index: batch.material_index,
|
material_index: batch.material_index,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if indices.is_empty() {
|
if indices.is_empty() {
|
||||||
@@ -183,6 +185,7 @@ mod tests {
|
|||||||
first_index: 0,
|
first_index: 0,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 0,
|
material_index: 0,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(mesh.vertices[1].position, [-0.8, -0.8]);
|
assert_eq!(mesh.vertices[1].position, [-0.8, -0.8]);
|
||||||
@@ -229,11 +232,13 @@ mod tests {
|
|||||||
first_index: 0,
|
first_index: 0,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 0,
|
material_index: 0,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
},
|
},
|
||||||
VulkanStaticDrawRange {
|
VulkanStaticDrawRange {
|
||||||
first_index: 3,
|
first_index: 3,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 7,
|
material_index: 7,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ impl VulkanSmokeRenderer {
|
|||||||
self.vertex_buffer_ref()?,
|
self.vertex_buffer_ref()?,
|
||||||
self.index_buffer_ref()?,
|
self.index_buffer_ref()?,
|
||||||
self.textures_ref()?,
|
self.textures_ref()?,
|
||||||
|
&self.draw_ranges,
|
||||||
reuse_command_pool,
|
reuse_command_pool,
|
||||||
)?,
|
)?,
|
||||||
|resources| destroy_swapchain_resources(device, self.command_pool, resources),
|
|resources| destroy_swapchain_resources(device, self.command_pool, resources),
|
||||||
@@ -631,11 +632,6 @@ impl VulkanSmokeRenderer {
|
|||||||
&render_pass_info,
|
&render_pass_info,
|
||||||
vk::SubpassContents::INLINE,
|
vk::SubpassContents::INLINE,
|
||||||
);
|
);
|
||||||
device.device().cmd_bind_pipeline(
|
|
||||||
command_buffer,
|
|
||||||
vk::PipelineBindPoint::GRAPHICS,
|
|
||||||
resources.pipeline,
|
|
||||||
);
|
|
||||||
let vertex_buffers = [self.vertex_buffer_ref()?.buffer];
|
let vertex_buffers = [self.vertex_buffer_ref()?.buffer];
|
||||||
let offsets = [0_u64];
|
let offsets = [0_u64];
|
||||||
device
|
device
|
||||||
@@ -648,6 +644,16 @@ impl VulkanSmokeRenderer {
|
|||||||
vk::IndexType::UINT16,
|
vk::IndexType::UINT16,
|
||||||
);
|
);
|
||||||
for (range, &texture_index) in self.draw_ranges.iter().zip(&self.draw_texture_indices) {
|
for (range, &texture_index) in self.draw_ranges.iter().zip(&self.draw_texture_indices) {
|
||||||
|
let pipeline = resources.pipelines.get(&range.pipeline_key()).ok_or(
|
||||||
|
VulkanSmokeRendererError::InvariantViolation {
|
||||||
|
context: "static draw pipeline variant",
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
device.device().cmd_bind_pipeline(
|
||||||
|
command_buffer,
|
||||||
|
vk::PipelineBindPoint::GRAPHICS,
|
||||||
|
*pipeline,
|
||||||
|
);
|
||||||
let descriptor_set = resources.descriptor_sets.get(texture_index).ok_or(
|
let descriptor_set = resources.descriptor_sets.get(texture_index).ok_or(
|
||||||
VulkanSmokeRendererError::InvariantViolation {
|
VulkanSmokeRendererError::InvariantViolation {
|
||||||
context: "static material descriptor set",
|
context: "static material descriptor set",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use ash::vk;
|
use ash::vk;
|
||||||
use fparkan_platform::{NativeWindowHandles, RenderRequest};
|
use fparkan_platform::{NativeWindowHandles, RenderRequest};
|
||||||
|
use fparkan_render::{LegacyPipelineState, PipelineKey};
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -70,6 +71,16 @@ pub struct VulkanStaticDrawRange {
|
|||||||
pub index_count: u32,
|
pub index_count: u32,
|
||||||
/// Original positional `Batch20.material_index` selector.
|
/// Original positional `Batch20.material_index` selector.
|
||||||
pub material_index: u16,
|
pub material_index: u16,
|
||||||
|
/// Backend-neutral fixed-function state for this source range.
|
||||||
|
pub pipeline_state: LegacyPipelineState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanStaticDrawRange {
|
||||||
|
/// Returns the canonical key used for Vulkan pipeline selection.
|
||||||
|
#[must_use]
|
||||||
|
pub fn pipeline_key(self) -> PipelineKey {
|
||||||
|
self.pipeline_state.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One diffuse material texture keyed by an original MSH batch selector.
|
/// One diffuse material texture keyed by an original MSH batch selector.
|
||||||
@@ -173,6 +184,7 @@ impl VulkanStaticMesh {
|
|||||||
first_index: 0,
|
first_index: 0,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 0,
|
material_index: 0,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,6 +251,7 @@ mod static_mesh_tests {
|
|||||||
first_index: 0,
|
first_index: 0,
|
||||||
index_count: 2,
|
index_count: 2,
|
||||||
material_index: 0,
|
material_index: 0,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
let out_of_range_index = VulkanStaticMesh {
|
let out_of_range_index = VulkanStaticMesh {
|
||||||
@@ -265,11 +278,13 @@ mod static_mesh_tests {
|
|||||||
first_index: 0,
|
first_index: 0,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 7,
|
material_index: 7,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
},
|
},
|
||||||
VulkanStaticDrawRange {
|
VulkanStaticDrawRange {
|
||||||
first_index: 3,
|
first_index: 3,
|
||||||
index_count: 3,
|
index_count: 3,
|
||||||
material_index: 2,
|
material_index: 2,
|
||||||
|
pipeline_state: LegacyPipelineState::default(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let texture = || VulkanStaticTexture {
|
let texture = || VulkanStaticTexture {
|
||||||
@@ -293,6 +308,21 @@ mod static_mesh_tests {
|
|||||||
Ok(vec![1, 0])
|
Ok(vec![1, 0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn draw_range_pipeline_key_follows_backend_neutral_state() {
|
||||||
|
let base = VulkanStaticMesh::smoke_triangle().draw_ranges[0];
|
||||||
|
let blended = VulkanStaticDrawRange {
|
||||||
|
pipeline_state: LegacyPipelineState {
|
||||||
|
blend: fparkan_render::LegacyBlendMode::SourceAlpha,
|
||||||
|
..LegacyPipelineState::default()
|
||||||
|
},
|
||||||
|
..base
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(base.pipeline_key().packed(), 0);
|
||||||
|
assert_ne!(base.pipeline_key(), blended.pipeline_key());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared bootstrap progress used to report partial renderer startup evidence.
|
/// Shared bootstrap progress used to report partial renderer startup evidence.
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use fparkan_render::{
|
||||||
|
LegacyBlendMode, LegacyCullMode, LegacyDepthMode, LegacyPipelineState, PipelineKey,
|
||||||
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
color_subresource_range, VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanLogicalDeviceProbe,
|
color_subresource_range, VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanLogicalDeviceProbe,
|
||||||
@@ -16,7 +20,8 @@ pub(super) struct VulkanSwapchainResources {
|
|||||||
pub(super) descriptor_pool: vk::DescriptorPool,
|
pub(super) descriptor_pool: vk::DescriptorPool,
|
||||||
pub(super) descriptor_sets: Vec<vk::DescriptorSet>,
|
pub(super) descriptor_sets: Vec<vk::DescriptorSet>,
|
||||||
pub(super) sampler: vk::Sampler,
|
pub(super) sampler: vk::Sampler,
|
||||||
pub(super) pipeline: vk::Pipeline,
|
/// Live graphics pipelines indexed by canonical backend-neutral state.
|
||||||
|
pub(super) pipelines: BTreeMap<PipelineKey, vk::Pipeline>,
|
||||||
pub(super) framebuffers: Vec<vk::Framebuffer>,
|
pub(super) framebuffers: Vec<vk::Framebuffer>,
|
||||||
pub(super) command_buffers: Vec<vk::CommandBuffer>,
|
pub(super) command_buffers: Vec<vk::CommandBuffer>,
|
||||||
}
|
}
|
||||||
@@ -28,11 +33,12 @@ struct PartialSwapchainResources {
|
|||||||
descriptor_set_layout: Option<vk::DescriptorSetLayout>,
|
descriptor_set_layout: Option<vk::DescriptorSetLayout>,
|
||||||
descriptor_pool: Option<vk::DescriptorPool>,
|
descriptor_pool: Option<vk::DescriptorPool>,
|
||||||
sampler: Option<vk::Sampler>,
|
sampler: Option<vk::Sampler>,
|
||||||
pipeline: Option<vk::Pipeline>,
|
pipelines: BTreeMap<PipelineKey, vk::Pipeline>,
|
||||||
framebuffers: Vec<vk::Framebuffer>,
|
framebuffers: Vec<vk::Framebuffer>,
|
||||||
command_buffers: Vec<vk::CommandBuffer>,
|
command_buffers: Vec<vk::CommandBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(super) fn create_swapchain_resources(
|
pub(super) fn create_swapchain_resources(
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
swapchain: &VulkanSwapchainProbe,
|
swapchain: &VulkanSwapchainProbe,
|
||||||
@@ -40,6 +46,7 @@ pub(super) fn create_swapchain_resources(
|
|||||||
vertex_buffer: &VulkanAllocatedBuffer,
|
vertex_buffer: &VulkanAllocatedBuffer,
|
||||||
index_buffer: &VulkanAllocatedBuffer,
|
index_buffer: &VulkanAllocatedBuffer,
|
||||||
textures: &[VulkanAllocatedImage],
|
textures: &[VulkanAllocatedImage],
|
||||||
|
draw_ranges: &[super::VulkanStaticDrawRange],
|
||||||
reuse_command_pool: bool,
|
reuse_command_pool: bool,
|
||||||
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
|
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
|
||||||
// SAFETY: The swapchain is live and owned by this renderer for the duration of the query.
|
// SAFETY: The swapchain is live and owned by this renderer for the duration of the query.
|
||||||
@@ -63,14 +70,14 @@ pub(super) fn create_swapchain_resources(
|
|||||||
descriptor_set_layout: None,
|
descriptor_set_layout: None,
|
||||||
descriptor_pool: None,
|
descriptor_pool: None,
|
||||||
sampler: None,
|
sampler: None,
|
||||||
pipeline: None,
|
pipelines: BTreeMap::new(),
|
||||||
framebuffers: Vec::new(),
|
framebuffers: Vec::new(),
|
||||||
command_buffers: Vec::new(),
|
command_buffers: Vec::new(),
|
||||||
};
|
};
|
||||||
let (
|
let (
|
||||||
render_pass,
|
render_pass,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
pipeline,
|
pipelines,
|
||||||
descriptor_set_layout,
|
descriptor_set_layout,
|
||||||
descriptor_pool,
|
descriptor_pool,
|
||||||
descriptor_sets,
|
descriptor_sets,
|
||||||
@@ -80,6 +87,7 @@ pub(super) fn create_swapchain_resources(
|
|||||||
swapchain.report.plan.format.format,
|
swapchain.report.plan.format.format,
|
||||||
swapchain.report.plan.extent,
|
swapchain.report.plan.extent,
|
||||||
textures,
|
textures,
|
||||||
|
draw_ranges,
|
||||||
) {
|
) {
|
||||||
Ok(bundle) => bundle,
|
Ok(bundle) => bundle,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -92,7 +100,7 @@ pub(super) fn create_swapchain_resources(
|
|||||||
partial.descriptor_set_layout = Some(descriptor_set_layout);
|
partial.descriptor_set_layout = Some(descriptor_set_layout);
|
||||||
partial.descriptor_pool = Some(descriptor_pool);
|
partial.descriptor_pool = Some(descriptor_pool);
|
||||||
partial.sampler = Some(sampler);
|
partial.sampler = Some(sampler);
|
||||||
partial.pipeline = Some(pipeline);
|
partial.pipelines = pipelines;
|
||||||
let framebuffers = match create_swapchain_framebuffers(
|
let framebuffers = match create_swapchain_framebuffers(
|
||||||
device,
|
device,
|
||||||
render_pass,
|
render_pass,
|
||||||
@@ -128,7 +136,7 @@ pub(super) fn create_swapchain_resources(
|
|||||||
descriptor_pool,
|
descriptor_pool,
|
||||||
descriptor_sets,
|
descriptor_sets,
|
||||||
sampler,
|
sampler,
|
||||||
pipeline,
|
pipelines: partial.pipelines,
|
||||||
framebuffers: partial.framebuffers,
|
framebuffers: partial.framebuffers,
|
||||||
command_buffers: partial.command_buffers,
|
command_buffers: partial.command_buffers,
|
||||||
})
|
})
|
||||||
@@ -152,11 +160,12 @@ fn create_swapchain_pipeline_bundle(
|
|||||||
format: i32,
|
format: i32,
|
||||||
extent: (u32, u32),
|
extent: (u32, u32),
|
||||||
textures: &[VulkanAllocatedImage],
|
textures: &[VulkanAllocatedImage],
|
||||||
|
draw_ranges: &[super::VulkanStaticDrawRange],
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
vk::RenderPass,
|
vk::RenderPass,
|
||||||
vk::PipelineLayout,
|
vk::PipelineLayout,
|
||||||
vk::Pipeline,
|
BTreeMap<PipelineKey, vk::Pipeline>,
|
||||||
vk::DescriptorSetLayout,
|
vk::DescriptorSetLayout,
|
||||||
vk::DescriptorPool,
|
vk::DescriptorPool,
|
||||||
Vec<vk::DescriptorSet>,
|
Vec<vk::DescriptorSet>,
|
||||||
@@ -184,8 +193,9 @@ fn create_swapchain_pipeline_bundle(
|
|||||||
device.device().destroy_render_pass(render_pass, None);
|
device.device().destroy_render_pass(render_pass, None);
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let pipeline = create_graphics_pipeline(device, render_pass, pipeline_layout, extent)
|
let pipelines =
|
||||||
.inspect_err(|_| {
|
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.
|
// SAFETY: These objects were created above on this live logical device and are destroyed on setup failure.
|
||||||
unsafe {
|
unsafe {
|
||||||
device
|
device
|
||||||
@@ -204,7 +214,7 @@ fn create_swapchain_pipeline_bundle(
|
|||||||
Ok((
|
Ok((
|
||||||
render_pass,
|
render_pass,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
pipeline,
|
pipelines,
|
||||||
descriptor_set_layout,
|
descriptor_set_layout,
|
||||||
descriptor_pool,
|
descriptor_pool,
|
||||||
descriptor_sets,
|
descriptor_sets,
|
||||||
@@ -457,13 +467,62 @@ fn extent_component_to_f32(value: u32) -> f32 {
|
|||||||
value as f32
|
value as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn create_graphics_pipeline_cache(
|
||||||
|
device: &VulkanLogicalDeviceProbe,
|
||||||
|
render_pass: vk::RenderPass,
|
||||||
|
pipeline_layout: vk::PipelineLayout,
|
||||||
|
extent: (u32, u32),
|
||||||
|
draw_ranges: &[super::VulkanStaticDrawRange],
|
||||||
|
) -> Result<BTreeMap<PipelineKey, vk::Pipeline>, VulkanSmokeRendererError> {
|
||||||
|
let mut pipelines = BTreeMap::new();
|
||||||
|
for range in draw_ranges {
|
||||||
|
let key = range.pipeline_key();
|
||||||
|
if pipelines.contains_key(&key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let pipeline = match create_graphics_pipeline(
|
||||||
|
device,
|
||||||
|
render_pass,
|
||||||
|
pipeline_layout,
|
||||||
|
extent,
|
||||||
|
range.pipeline_state,
|
||||||
|
) {
|
||||||
|
Ok(pipeline) => pipeline,
|
||||||
|
Err(error) => {
|
||||||
|
// SAFETY: All previously created pipelines belong to this live device and are rolled back with their bundle.
|
||||||
|
unsafe {
|
||||||
|
for pipeline in pipelines.into_values() {
|
||||||
|
device.device().destroy_pipeline(pipeline, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pipelines.insert(key, pipeline);
|
||||||
|
}
|
||||||
|
Ok(pipelines)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn create_graphics_pipeline(
|
fn create_graphics_pipeline(
|
||||||
device: &VulkanLogicalDeviceProbe,
|
device: &VulkanLogicalDeviceProbe,
|
||||||
render_pass: vk::RenderPass,
|
render_pass: vk::RenderPass,
|
||||||
pipeline_layout: vk::PipelineLayout,
|
pipeline_layout: vk::PipelineLayout,
|
||||||
extent: (u32, u32),
|
extent: (u32, u32),
|
||||||
|
state: LegacyPipelineState,
|
||||||
) -> Result<vk::Pipeline, VulkanSmokeRendererError> {
|
) -> Result<vk::Pipeline, VulkanSmokeRendererError> {
|
||||||
|
if state.depth != LegacyDepthMode::Disabled {
|
||||||
|
return Err(VulkanSmokeRendererError::InvalidStaticMesh {
|
||||||
|
context: "static renderer has no depth attachment for requested pipeline state",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state.alpha_test {
|
||||||
|
return Err(VulkanSmokeRendererError::InvalidStaticMesh {
|
||||||
|
context:
|
||||||
|
"static renderer has no alpha-test shader variant for requested pipeline state",
|
||||||
|
});
|
||||||
|
}
|
||||||
let vertex_shader = create_shader_module(device, TRIANGLE_VERTEX_SHADER_WORDS)?;
|
let vertex_shader = create_shader_module(device, TRIANGLE_VERTEX_SHADER_WORDS)?;
|
||||||
let fragment_shader = match create_shader_module(device, TRIANGLE_FRAGMENT_SHADER_WORDS) {
|
let fragment_shader = match create_shader_module(device, TRIANGLE_FRAGMENT_SHADER_WORDS) {
|
||||||
Ok(module) => module,
|
Ok(module) => module,
|
||||||
@@ -535,9 +594,11 @@ fn create_graphics_pipeline(
|
|||||||
.rasterizer_discard_enable(false)
|
.rasterizer_discard_enable(false)
|
||||||
.polygon_mode(vk::PolygonMode::FILL)
|
.polygon_mode(vk::PolygonMode::FILL)
|
||||||
.line_width(1.0)
|
.line_width(1.0)
|
||||||
// Static MSH viewer mode has no original material/cull state yet; draw
|
.cull_mode(match state.cull {
|
||||||
// both windings so every source batch is visible during the bridge.
|
LegacyCullMode::Disabled => vk::CullModeFlags::NONE,
|
||||||
.cull_mode(vk::CullModeFlags::NONE)
|
LegacyCullMode::BackFace => vk::CullModeFlags::BACK,
|
||||||
|
LegacyCullMode::FrontFace => vk::CullModeFlags::FRONT,
|
||||||
|
})
|
||||||
.front_face(vk::FrontFace::CLOCKWISE)
|
.front_face(vk::FrontFace::CLOCKWISE)
|
||||||
.depth_bias_enable(false);
|
.depth_bias_enable(false);
|
||||||
let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
|
let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
|
||||||
@@ -550,7 +611,13 @@ fn create_graphics_pipeline(
|
|||||||
| vk::ColorComponentFlags::B
|
| vk::ColorComponentFlags::B
|
||||||
| vk::ColorComponentFlags::A,
|
| vk::ColorComponentFlags::A,
|
||||||
)
|
)
|
||||||
.blend_enable(false);
|
.blend_enable(state.blend == LegacyBlendMode::SourceAlpha)
|
||||||
|
.src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
|
||||||
|
.dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||||
|
.color_blend_op(vk::BlendOp::ADD)
|
||||||
|
.src_alpha_blend_factor(vk::BlendFactor::ONE)
|
||||||
|
.dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||||
|
.alpha_blend_op(vk::BlendOp::ADD);
|
||||||
let color_blend_attachments = [color_blend_attachment];
|
let color_blend_attachments = [color_blend_attachment];
|
||||||
let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default()
|
let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default()
|
||||||
.logic_op_enable(false)
|
.logic_op_enable(false)
|
||||||
@@ -655,7 +722,9 @@ pub(super) fn destroy_swapchain_resources(
|
|||||||
for framebuffer in resources.framebuffers {
|
for framebuffer in resources.framebuffers {
|
||||||
device.device().destroy_framebuffer(framebuffer, None);
|
device.device().destroy_framebuffer(framebuffer, None);
|
||||||
}
|
}
|
||||||
device.device().destroy_pipeline(resources.pipeline, None);
|
for pipeline in resources.pipelines.into_values() {
|
||||||
|
device.device().destroy_pipeline(pipeline, None);
|
||||||
|
}
|
||||||
device
|
device
|
||||||
.device()
|
.device()
|
||||||
.destroy_pipeline_layout(resources.pipeline_layout, None);
|
.destroy_pipeline_layout(resources.pipeline_layout, None);
|
||||||
@@ -690,7 +759,7 @@ fn destroy_partial_swapchain_resources(
|
|||||||
for framebuffer in partial.framebuffers {
|
for framebuffer in partial.framebuffers {
|
||||||
device.device().destroy_framebuffer(framebuffer, None);
|
device.device().destroy_framebuffer(framebuffer, None);
|
||||||
}
|
}
|
||||||
if let Some(pipeline) = partial.pipeline {
|
for pipeline in partial.pipelines.into_values() {
|
||||||
device.device().destroy_pipeline(pipeline, None);
|
device.device().destroy_pipeline(pipeline, None);
|
||||||
}
|
}
|
||||||
if let Some(pipeline_layout) = partial.pipeline_layout {
|
if let Some(pipeline_layout) = partial.pipeline_layout {
|
||||||
|
|||||||
+14
-8
@@ -838,14 +838,20 @@ draw_indexed(item.batch.base_vertex,
|
|||||||
от версии Rust или процесса. Alpha reference намеренно не входит в key: это
|
от версии Rust или процесса. Alpha reference намеренно не входит в key: это
|
||||||
dynamic material constant, а не структурный вариант graphics pipeline.
|
dynamic material constant, а не структурный вариант graphics pipeline.
|
||||||
|
|
||||||
Пока текущий статический Vulkan viewer создаёт только базовое состояние
|
Текущий static Vulkan viewer дедуплицирует состояния ranges при каждой сборке
|
||||||
`opaque + depth disabled + cull disabled + alpha-test disabled`; ключ уже
|
swapchain resources, создаёт один `vk::Pipeline` на уникальный `PipelineKey` и
|
||||||
передаётся через render command и canonical capture, но Vulkan pipeline cache
|
выбирает его непосредственно перед соответствующим `vkCmdDrawIndexed`.
|
||||||
ещё не выбирает variants по нему. Также не установлен источник значений для
|
Кэш корректно уничтожается при recreate/teardown; setup failure откатывает уже
|
||||||
Batch20/MAT0: их поля нельзя объявлять blend/depth/cull mapping без dynamic
|
созданные variants. Baseline оригинального MSH подтверждён с состоянием
|
||||||
capture или дополнительного дизассемблирования. Поэтому этот контракт —
|
`opaque + depth disabled + cull disabled + alpha-test disabled`.
|
||||||
подготовленная граница совместимости, а не заявление о готовой parity
|
|
||||||
fixed-function state.
|
`SourceAlpha` и front/back culling имеют Vulkan mapping в этом кэше. Запрос
|
||||||
|
depth или alpha-test сейчас завершается явной ошибкой: renderer ещё не имеет
|
||||||
|
depth attachment либо alpha-test shader variant, поэтому он не должен молча
|
||||||
|
подменять state. Также не установлен источник значений для Batch20/MAT0: их
|
||||||
|
поля нельзя объявлять blend/depth/cull mapping без dynamic capture или
|
||||||
|
дополнительного дизассемблирования. Это частично реализованная compatibility
|
||||||
|
boundary, а не заявление о готовой parity fixed-function state.
|
||||||
|
|
||||||
После последнего world pass renderer закрывает сцену и выводит back buffer.
|
После последнего world pass renderer закрывает сцену и выводит back buffer.
|
||||||
World3D снимает `in_render`, восстанавливает временный viewport state и вызывает
|
World3D снимает `in_render`, восстанавливает временный viewport state и вызывает
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ S2-PLANNING-RENDER-009 covered cargo xtask policy
|
|||||||
S3-VK-MESH-UPLOAD-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-mtcheck-msh.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH; original MSH reports 128 vertices and 252 indices
|
S3-VK-MESH-UPLOAD-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-mtcheck-msh.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH; original MSH reports 128 vertices and 252 indices
|
||||||
S3-VK-TEXM-UPLOAD-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-mtcheck-texm-upload.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH --texture-root <GOG_ROOT> --texture-archive Textures.lib --texture-name DEFAULT.0; original TEXM 16x16 decoded RGBA8, staged into device-local image and transitioned to SHADER_READ_ONLY_OPTIMAL
|
S3-VK-TEXM-UPLOAD-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-mtcheck-texm-upload.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH --texture-root <GOG_ROOT> --texture-archive Textures.lib --texture-name DEFAULT.0; original TEXM 16x16 decoded RGBA8, staged into device-local image and transitioned to SHADER_READ_ONLY_OPTIMAL
|
||||||
S3-VK-DESCRIPTOR-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-texm-sampled.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH --texture-root <GOG_ROOT> --texture-archive Textures.lib --texture-name DEFAULT.0; original TEXM is bound as set=0,binding=0 combined image sampler and sampled in fragment shader; validation_warning_count=0 and validation_error_count=0
|
S3-VK-DESCRIPTOR-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-texm-sampled.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH --texture-root <GOG_ROOT> --texture-archive Textures.lib --texture-name DEFAULT.0; original TEXM is bound as set=0,binding=0 combined image sampler and sampled in fragment shader; validation_warning_count=0 and validation_error_count=0
|
||||||
S3-VK-PIPELINE-001 blocked awaits Stage 3 graphics pipeline key and cache implementation
|
S3-VK-PIPELINE-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-pipeline-key-cache-smoke.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive system.rlb --model-name MTCHECK.MSH --texture-root <GOG_ROOT> --texture-archive Textures.lib --texture-name DEFAULT.0; PipelineKey selects a deduplicated live Vulkan pipeline per static draw range (baseline key only); unproven Batch20/MAT0 state mapping and unsupported depth/alpha-test variants remain explicit gaps
|
||||||
S3-VK-DRAW-MODEL-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-fr-l-mtp-per-batch-materials.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive fortif.rlb --model-name FR_L_MTP.msh --wear-root <GOG_ROOT> --wear-archive fortif.rlb --wear-name FR_L_MTP.WEA; live Win32 Vulkan issues 237 source-preserving indexed batch draws and binds the deduplicated WEAR→MAT0 material descriptor selected by each Batch20.material_index (this corpus model has selector 0 only)
|
S3-VK-DRAW-MODEL-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-fr-l-mtp-per-batch-materials.json --frames 300 --resize-frame 120 --timeout-seconds 90 --model-root <GOG_ROOT> --model-archive fortif.rlb --model-name FR_L_MTP.msh --wear-root <GOG_ROOT> --wear-archive fortif.rlb --wear-name FR_L_MTP.WEA; live Win32 Vulkan issues 237 source-preserving indexed batch draws and binds the deduplicated WEAR→MAT0 material descriptor selected by each Batch20.material_index (this corpus model has selector 0 only)
|
||||||
S3-VK-DRAW-TERRAIN-001 blocked awaits Stage 3 terrain Vulkan draw path
|
S3-VK-DRAW-TERRAIN-001 blocked awaits Stage 3 terrain Vulkan draw path
|
||||||
S3-VK-PIXEL-CAPTURE-001 blocked awaits Stage 3 fixed-camera pixel capture approval flow
|
S3-VK-PIXEL-CAPTURE-001 blocked awaits Stage 3 fixed-camera pixel capture approval flow
|
||||||
|
|||||||
|
Reference in New Issue
Block a user