feat(vulkan): add depth attachment pipeline support
This commit is contained in:
@@ -53,10 +53,10 @@ pub use self::instance::{
|
||||
#[cfg(test)]
|
||||
use self::instance::{cstring_vec, ensure_instance_extensions_available};
|
||||
use self::resources::{
|
||||
color_subresource_range, create_command_pool, create_frame_sync,
|
||||
color_subresource_range, create_command_pool, create_depth_attachment, create_frame_sync,
|
||||
create_static_mesh_index_buffer, create_static_mesh_vertex_buffer, create_static_texture_image,
|
||||
destroy_allocated_buffer, destroy_allocated_image, VulkanAllocatedBuffer, VulkanAllocatedImage,
|
||||
VulkanFrameSync,
|
||||
destroy_allocated_buffer, destroy_allocated_image, destroy_depth_attachment,
|
||||
VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanDepthAttachment, VulkanFrameSync,
|
||||
};
|
||||
pub use self::runtime::{
|
||||
create_vulkan_logical_device_probe, create_vulkan_logical_device_probe_for_request,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use ash::vk;
|
||||
use fparkan_platform::DepthStencilSupport;
|
||||
|
||||
use super::{
|
||||
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanStaticMesh,
|
||||
VulkanStaticTexture,
|
||||
};
|
||||
use crate::select_depth_stencil_attachment_format;
|
||||
|
||||
pub(super) struct VulkanAllocatedBuffer {
|
||||
pub(super) buffer: vk::Buffer,
|
||||
@@ -18,6 +20,171 @@ pub(super) struct VulkanAllocatedImage {
|
||||
pub(super) view: vk::ImageView,
|
||||
}
|
||||
|
||||
/// Depth/stencil image and the exact format selected for its render pass.
|
||||
pub(super) struct VulkanDepthAttachment {
|
||||
pub(super) image: VulkanAllocatedImage,
|
||||
pub(super) format: vk::Format,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn create_depth_attachment(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
extent: (u32, u32),
|
||||
request: DepthStencilSupport,
|
||||
) -> Result<VulkanDepthAttachment, VulkanSmokeRendererError> {
|
||||
let supported_formats = depth_attachment_formats(instance, device);
|
||||
let format = select_depth_stencil_attachment_format(&supported_formats, request)
|
||||
.map(vk::Format::from_raw)
|
||||
.ok_or(VulkanSmokeRendererError::InvalidStaticMesh {
|
||||
context: "logical device has no selected depth attachment format",
|
||||
})?;
|
||||
let image_info = vk::ImageCreateInfo::default()
|
||||
.image_type(vk::ImageType::TYPE_2D)
|
||||
.format(format)
|
||||
.extent(vk::Extent3D {
|
||||
width: extent.0,
|
||||
height: extent.1,
|
||||
depth: 1,
|
||||
})
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.samples(vk::SampleCountFlags::TYPE_1)
|
||||
.tiling(vk::ImageTiling::OPTIMAL)
|
||||
.usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||
.sharing_mode(vk::SharingMode::EXCLUSIVE)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED);
|
||||
// SAFETY: The creation info is stack-owned and uses the live non-zero swapchain extent.
|
||||
let image = unsafe { device.device().create_image(&image_info, None) }.map_err(|result| {
|
||||
VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkCreateImage(depth attachment)",
|
||||
result,
|
||||
}
|
||||
})?;
|
||||
// SAFETY: The newly created image belongs to this device.
|
||||
let requirements = unsafe { device.device().get_image_memory_requirements(image) };
|
||||
let Some(memory_type_index) = find_memory_type(
|
||||
instance,
|
||||
device.physical_device(),
|
||||
requirements.memory_type_bits,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
) else {
|
||||
// SAFETY: The unbound image is rolled back on its owning device.
|
||||
unsafe { device.device().destroy_image(image, None) };
|
||||
return Err(VulkanSmokeRendererError::MissingMemoryType {
|
||||
context: "depth attachment",
|
||||
});
|
||||
};
|
||||
let allocation = vk::MemoryAllocateInfo::default()
|
||||
.allocation_size(requirements.size)
|
||||
.memory_type_index(memory_type_index);
|
||||
// SAFETY: Allocation parameters are derived from this image's requirements.
|
||||
let allocation_result = unsafe { device.device().allocate_memory(&allocation, None) };
|
||||
let memory = match allocation_result {
|
||||
Ok(memory) => memory,
|
||||
Err(result) => {
|
||||
// SAFETY: The unbound image is rolled back on allocation failure.
|
||||
unsafe { device.device().destroy_image(image, None) };
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkAllocateMemory(depth attachment)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
};
|
||||
// SAFETY: The allocation satisfies this image's queried requirements.
|
||||
if let Err(result) = unsafe { device.device().bind_image_memory(image, memory, 0) } {
|
||||
// SAFETY: Both newly created resources are rolled back together.
|
||||
unsafe {
|
||||
device.device().destroy_image(image, None);
|
||||
device.device().free_memory(memory, None);
|
||||
}
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkBindImageMemory(depth attachment)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
let range = vk::ImageSubresourceRange::default()
|
||||
.aspect_mask(depth_aspect(format))
|
||||
.base_mip_level(0)
|
||||
.level_count(1)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1);
|
||||
let view_info = vk::ImageViewCreateInfo::default()
|
||||
.image(image)
|
||||
.view_type(vk::ImageViewType::TYPE_2D)
|
||||
.format(format)
|
||||
.subresource_range(range);
|
||||
// SAFETY: The image is bound and the depth/stencil aspect matches its selected format.
|
||||
let view_result = unsafe { device.device().create_image_view(&view_info, None) };
|
||||
let view = match view_result {
|
||||
Ok(view) => view,
|
||||
Err(result) => {
|
||||
// SAFETY: Both newly created resources are rolled back together.
|
||||
unsafe {
|
||||
device.device().destroy_image(image, None);
|
||||
device.device().free_memory(memory, None);
|
||||
}
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkCreateImageView(depth attachment)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
};
|
||||
Ok(VulkanDepthAttachment {
|
||||
image: VulkanAllocatedImage {
|
||||
image,
|
||||
memory,
|
||||
view,
|
||||
},
|
||||
format,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn destroy_depth_attachment(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
attachment: &VulkanDepthAttachment,
|
||||
) {
|
||||
destroy_allocated_image(device, &attachment.image);
|
||||
}
|
||||
|
||||
fn depth_attachment_formats(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
) -> Vec<i32> {
|
||||
[
|
||||
vk::Format::D16_UNORM,
|
||||
vk::Format::X8_D24_UNORM_PACK32,
|
||||
vk::Format::D32_SFLOAT,
|
||||
vk::Format::D16_UNORM_S8_UINT,
|
||||
vk::Format::D24_UNORM_S8_UINT,
|
||||
vk::Format::D32_SFLOAT_S8_UINT,
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|format| {
|
||||
// SAFETY: The physical device belongs to this instance and the query returns a value copy.
|
||||
unsafe {
|
||||
instance
|
||||
.instance
|
||||
.get_physical_device_format_properties(device.physical_device(), *format)
|
||||
}
|
||||
.optimal_tiling_features
|
||||
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||
})
|
||||
.map(vk::Format::as_raw)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn depth_aspect(format: vk::Format) -> vk::ImageAspectFlags {
|
||||
match format {
|
||||
vk::Format::D16_UNORM_S8_UINT
|
||||
| vk::Format::D24_UNORM_S8_UINT
|
||||
| vk::Format::D32_SFLOAT_S8_UINT => {
|
||||
vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL
|
||||
}
|
||||
_ => vk::ImageAspectFlags::DEPTH,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct VulkanFrameSync {
|
||||
pub(super) image_available: vk::Semaphore,
|
||||
pub(super) render_finished: vk::Semaphore,
|
||||
|
||||
@@ -228,6 +228,7 @@ impl VulkanSmokeRenderer {
|
||||
frame_sync: Vec::new(),
|
||||
images_in_flight: Vec::new(),
|
||||
current_frame: 0,
|
||||
depth_request: create_info.render_request.depth,
|
||||
pending_extent: None,
|
||||
swapchain_recreate_count: 0,
|
||||
report: VulkanSmokeRendererReport {
|
||||
@@ -554,6 +555,7 @@ impl VulkanSmokeRenderer {
|
||||
let swapchain = self.swapchain_ref()?;
|
||||
let resources = RollbackOnDrop::new(
|
||||
create_swapchain_resources(
|
||||
self.instance_ref()?,
|
||||
device,
|
||||
swapchain,
|
||||
self.command_pool,
|
||||
@@ -561,6 +563,7 @@ impl VulkanSmokeRenderer {
|
||||
self.index_buffer_ref()?,
|
||||
self.textures_ref()?,
|
||||
&self.draw_ranges,
|
||||
self.depth_request,
|
||||
reuse_command_pool,
|
||||
)?,
|
||||
|resources| destroy_swapchain_resources(device, self.command_pool, resources),
|
||||
@@ -608,11 +611,19 @@ impl VulkanSmokeRenderer {
|
||||
result: error,
|
||||
})?;
|
||||
|
||||
let clear_values = [vk::ClearValue {
|
||||
let clear_values = [
|
||||
vk::ClearValue {
|
||||
color: vk::ClearColorValue {
|
||||
float32: [0.05, 0.08, 0.11, 1.0],
|
||||
},
|
||||
}];
|
||||
},
|
||||
vk::ClearValue {
|
||||
depth_stencil: vk::ClearDepthStencilValue {
|
||||
depth: 1.0,
|
||||
stencil: 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
let render_area = vk::Rect2D {
|
||||
offset: vk::Offset2D { x: 0, y: 0 },
|
||||
extent: vk::Extent2D {
|
||||
|
||||
@@ -543,6 +543,7 @@ pub struct VulkanSmokeRenderer {
|
||||
pub(super) frame_sync: Vec<VulkanFrameSync>,
|
||||
pub(super) images_in_flight: Vec<vk::Fence>,
|
||||
pub(super) current_frame: usize,
|
||||
pub(super) depth_request: fparkan_platform::DepthStencilSupport,
|
||||
pub(super) pending_extent: Option<(u32, u32)>,
|
||||
pub(super) swapchain_recreate_count: u32,
|
||||
pub(super) report: VulkanSmokeRendererReport,
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use ash::vk;
|
||||
use fparkan_platform::DepthStencilSupport;
|
||||
use fparkan_render::{
|
||||
LegacyBlendMode, LegacyCullMode, LegacyDepthMode, LegacyPipelineState, PipelineKey,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::{
|
||||
color_subresource_range, VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanLogicalDeviceProbe,
|
||||
VulkanSmokeRendererError, VulkanSwapchainProbe, TRIANGLE_FRAGMENT_SHADER_WORDS,
|
||||
TRIANGLE_VERTEX_SHADER_WORDS,
|
||||
color_subresource_range, create_depth_attachment, destroy_depth_attachment,
|
||||
VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanDepthAttachment, VulkanInstanceProbe,
|
||||
VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanSwapchainProbe,
|
||||
TRIANGLE_FRAGMENT_SHADER_WORDS, TRIANGLE_VERTEX_SHADER_WORDS,
|
||||
};
|
||||
|
||||
pub(super) struct VulkanSwapchainResources {
|
||||
pub(super) image_views: Vec<vk::ImageView>,
|
||||
pub(super) depth_attachment: VulkanDepthAttachment,
|
||||
pub(super) render_pass: vk::RenderPass,
|
||||
pub(super) pipeline_layout: vk::PipelineLayout,
|
||||
pub(super) descriptor_set_layout: vk::DescriptorSetLayout,
|
||||
@@ -28,6 +31,7 @@ pub(super) struct VulkanSwapchainResources {
|
||||
|
||||
struct PartialSwapchainResources {
|
||||
image_views: Vec<vk::ImageView>,
|
||||
depth_attachment: Option<VulkanDepthAttachment>,
|
||||
render_pass: Option<vk::RenderPass>,
|
||||
pipeline_layout: Option<vk::PipelineLayout>,
|
||||
descriptor_set_layout: Option<vk::DescriptorSetLayout>,
|
||||
@@ -38,8 +42,9 @@ struct PartialSwapchainResources {
|
||||
command_buffers: Vec<vk::CommandBuffer>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||
pub(super) fn create_swapchain_resources(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
swapchain: &VulkanSwapchainProbe,
|
||||
command_pool: vk::CommandPool,
|
||||
@@ -47,6 +52,7 @@ pub(super) fn create_swapchain_resources(
|
||||
index_buffer: &VulkanAllocatedBuffer,
|
||||
textures: &[VulkanAllocatedImage],
|
||||
draw_ranges: &[super::VulkanStaticDrawRange],
|
||||
depth_request: DepthStencilSupport,
|
||||
reuse_command_pool: bool,
|
||||
) -> Result<VulkanSwapchainResources, VulkanSmokeRendererError> {
|
||||
// SAFETY: The swapchain is live and owned by this renderer for the duration of the query.
|
||||
@@ -65,6 +71,7 @@ pub(super) fn create_swapchain_resources(
|
||||
&images,
|
||||
swapchain.report.plan.format.format,
|
||||
)?,
|
||||
depth_attachment: None,
|
||||
render_pass: None,
|
||||
pipeline_layout: None,
|
||||
descriptor_set_layout: None,
|
||||
@@ -75,6 +82,7 @@ pub(super) fn create_swapchain_resources(
|
||||
command_buffers: Vec::new(),
|
||||
};
|
||||
let (
|
||||
depth_attachment,
|
||||
render_pass,
|
||||
pipeline_layout,
|
||||
pipelines,
|
||||
@@ -83,11 +91,13 @@ pub(super) fn create_swapchain_resources(
|
||||
descriptor_sets,
|
||||
sampler,
|
||||
) = match create_swapchain_pipeline_bundle(
|
||||
instance,
|
||||
device,
|
||||
swapchain.report.plan.format.format,
|
||||
swapchain.report.plan.extent,
|
||||
textures,
|
||||
draw_ranges,
|
||||
depth_request,
|
||||
) {
|
||||
Ok(bundle) => bundle,
|
||||
Err(error) => {
|
||||
@@ -95,6 +105,8 @@ pub(super) fn create_swapchain_resources(
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let depth_view = depth_attachment.image.view;
|
||||
partial.depth_attachment = Some(depth_attachment);
|
||||
partial.render_pass = Some(render_pass);
|
||||
partial.pipeline_layout = Some(pipeline_layout);
|
||||
partial.descriptor_set_layout = Some(descriptor_set_layout);
|
||||
@@ -105,6 +117,7 @@ pub(super) fn create_swapchain_resources(
|
||||
device,
|
||||
render_pass,
|
||||
&partial.image_views,
|
||||
depth_view,
|
||||
swapchain.report.plan.extent,
|
||||
) {
|
||||
Ok(framebuffers) => framebuffers,
|
||||
@@ -128,8 +141,16 @@ pub(super) fn create_swapchain_resources(
|
||||
};
|
||||
partial.command_buffers = command_buffers;
|
||||
let _ = (vertex_buffer, index_buffer);
|
||||
let depth_attachment =
|
||||
partial
|
||||
.depth_attachment
|
||||
.take()
|
||||
.ok_or(VulkanSmokeRendererError::InvariantViolation {
|
||||
context: "depth attachment ownership after swapchain setup",
|
||||
})?;
|
||||
Ok(VulkanSwapchainResources {
|
||||
image_views: partial.image_views,
|
||||
depth_attachment,
|
||||
render_pass,
|
||||
pipeline_layout,
|
||||
descriptor_set_layout,
|
||||
@@ -156,13 +177,16 @@ fn create_swapchain_image_views(
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn create_swapchain_pipeline_bundle(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
format: i32,
|
||||
extent: (u32, u32),
|
||||
textures: &[VulkanAllocatedImage],
|
||||
draw_ranges: &[super::VulkanStaticDrawRange],
|
||||
depth_request: DepthStencilSupport,
|
||||
) -> Result<
|
||||
(
|
||||
VulkanDepthAttachment,
|
||||
vk::RenderPass,
|
||||
vk::PipelineLayout,
|
||||
BTreeMap<PipelineKey, vk::Pipeline>,
|
||||
@@ -173,11 +197,19 @@ fn create_swapchain_pipeline_bundle(
|
||||
),
|
||||
VulkanSmokeRendererError,
|
||||
> {
|
||||
let render_pass = create_render_pass(device, format)?;
|
||||
let depth_attachment = create_depth_attachment(instance, device, extent, depth_request)?;
|
||||
let render_pass = match create_render_pass(device, format, depth_attachment.format) {
|
||||
Ok(render_pass) => render_pass,
|
||||
Err(error) => {
|
||||
destroy_depth_attachment(device, &depth_attachment);
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let (descriptor_set_layout, descriptor_pool, descriptor_sets, sampler) =
|
||||
create_texture_descriptor_bundle(device, textures).inspect_err(|_| {
|
||||
// SAFETY: The render pass was created above on this live logical device and is destroyed on setup failure.
|
||||
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(|_| {
|
||||
@@ -192,6 +224,7 @@ fn create_swapchain_pipeline_bundle(
|
||||
.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, render_pass, pipeline_layout, extent, draw_ranges)
|
||||
@@ -210,8 +243,10 @@ fn create_swapchain_pipeline_bundle(
|
||||
.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,
|
||||
pipeline_layout,
|
||||
pipelines,
|
||||
@@ -226,11 +261,12 @@ fn create_swapchain_framebuffers(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
render_pass: vk::RenderPass,
|
||||
image_views: &[vk::ImageView],
|
||||
depth_view: vk::ImageView,
|
||||
extent: (u32, u32),
|
||||
) -> Result<Vec<vk::Framebuffer>, VulkanSmokeRendererError> {
|
||||
let mut framebuffers = Vec::with_capacity(image_views.len());
|
||||
for image_view in image_views.iter().copied() {
|
||||
match create_framebuffer(device, render_pass, image_view, extent) {
|
||||
match create_framebuffer(device, render_pass, image_view, depth_view, extent) {
|
||||
Ok(framebuffer) => framebuffers.push(framebuffer),
|
||||
Err(error) => {
|
||||
// SAFETY: These framebuffers were created above on this live logical device and are destroyed on setup failure.
|
||||
@@ -288,6 +324,7 @@ fn create_image_view(
|
||||
fn create_render_pass(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
format: i32,
|
||||
depth_format: vk::Format,
|
||||
) -> Result<vk::RenderPass, VulkanSmokeRendererError> {
|
||||
let color_attachment = vk::AttachmentDescription::default()
|
||||
.format(vk::Format::from_raw(format))
|
||||
@@ -299,10 +336,23 @@ fn create_render_pass(
|
||||
let color_attachment_ref = vk::AttachmentReference::default()
|
||||
.attachment(0)
|
||||
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
|
||||
let depth_attachment = vk::AttachmentDescription::default()
|
||||
.format(depth_format)
|
||||
.samples(vk::SampleCountFlags::TYPE_1)
|
||||
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||
.store_op(vk::AttachmentStoreOp::DONT_CARE)
|
||||
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
|
||||
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED)
|
||||
.final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
let depth_attachment_ref = vk::AttachmentReference::default()
|
||||
.attachment(1)
|
||||
.layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
let color_attachments = [color_attachment_ref];
|
||||
let subpass = vk::SubpassDescription::default()
|
||||
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
|
||||
.color_attachments(&color_attachments);
|
||||
.color_attachments(&color_attachments)
|
||||
.depth_stencil_attachment(&depth_attachment_ref);
|
||||
let dependency = vk::SubpassDependency::default()
|
||||
.src_subpass(vk::SUBPASS_EXTERNAL)
|
||||
.dst_subpass(0)
|
||||
@@ -310,7 +360,7 @@ fn create_render_pass(
|
||||
.dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT)
|
||||
.src_access_mask(vk::AccessFlags::empty())
|
||||
.dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE);
|
||||
let attachments = [color_attachment];
|
||||
let attachments = [color_attachment, depth_attachment];
|
||||
let subpasses = [subpass];
|
||||
let dependencies = [dependency];
|
||||
let create_info = vk::RenderPassCreateInfo::default()
|
||||
@@ -512,11 +562,6 @@ fn create_graphics_pipeline(
|
||||
extent: (u32, u32),
|
||||
state: LegacyPipelineState,
|
||||
) -> 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:
|
||||
@@ -604,6 +649,12 @@ fn create_graphics_pipeline(
|
||||
let multisample_state = vk::PipelineMultisampleStateCreateInfo::default()
|
||||
.sample_shading_enable(false)
|
||||
.rasterization_samples(vk::SampleCountFlags::TYPE_1);
|
||||
let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::default()
|
||||
.depth_test_enable(state.depth != LegacyDepthMode::Disabled)
|
||||
.depth_write_enable(state.depth == LegacyDepthMode::TestWrite)
|
||||
.depth_compare_op(vk::CompareOp::LESS_OR_EQUAL)
|
||||
.depth_bounds_test_enable(false)
|
||||
.stencil_test_enable(false);
|
||||
let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default()
|
||||
.color_write_mask(
|
||||
vk::ColorComponentFlags::R
|
||||
@@ -629,6 +680,7 @@ fn create_graphics_pipeline(
|
||||
.viewport_state(&viewport_state)
|
||||
.rasterization_state(&rasterization_state)
|
||||
.multisample_state(&multisample_state)
|
||||
.depth_stencil_state(&depth_stencil_state)
|
||||
.color_blend_state(&color_blend_state)
|
||||
.layout(pipeline_layout)
|
||||
.render_pass(render_pass)
|
||||
@@ -671,9 +723,10 @@ fn create_framebuffer(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
render_pass: vk::RenderPass,
|
||||
image_view: vk::ImageView,
|
||||
depth_view: vk::ImageView,
|
||||
extent: (u32, u32),
|
||||
) -> Result<vk::Framebuffer, VulkanSmokeRendererError> {
|
||||
let attachments = [image_view];
|
||||
let attachments = [image_view, depth_view];
|
||||
let create_info = vk::FramebufferCreateInfo::default()
|
||||
.render_pass(render_pass)
|
||||
.attachments(&attachments)
|
||||
@@ -738,6 +791,7 @@ pub(super) fn destroy_swapchain_resources(
|
||||
device
|
||||
.device()
|
||||
.destroy_render_pass(resources.render_pass, None);
|
||||
destroy_depth_attachment(device, &resources.depth_attachment);
|
||||
for image_view in resources.image_views {
|
||||
device.device().destroy_image_view(image_view, None);
|
||||
}
|
||||
@@ -783,6 +837,9 @@ fn destroy_partial_swapchain_resources(
|
||||
if let Some(render_pass) = partial.render_pass {
|
||||
device.device().destroy_render_pass(render_pass, None);
|
||||
}
|
||||
if let Some(depth_attachment) = partial.depth_attachment {
|
||||
destroy_depth_attachment(device, &depth_attachment);
|
||||
}
|
||||
for image_view in partial.image_views {
|
||||
device.device().destroy_image_view(image_view, None);
|
||||
}
|
||||
|
||||
+10
-7
@@ -845,20 +845,23 @@ swapchain resources, создаёт один `vk::Pipeline` на уникаль
|
||||
созданные variants. Baseline оригинального MSH подтверждён с состоянием
|
||||
`opaque + depth disabled + cull disabled + alpha-test disabled`.
|
||||
|
||||
`SourceAlpha` и front/back culling имеют Vulkan mapping в этом кэше. Запрос
|
||||
depth или alpha-test сейчас завершается явной ошибкой: renderer ещё не имеет
|
||||
depth attachment либо alpha-test shader variant, поэтому он не должен молча
|
||||
подменять state. Также не установлен источник значений для Batch20/MAT0: их
|
||||
`SourceAlpha`, front/back culling и depth modes имеют Vulkan mapping в этом
|
||||
кэше. При создании swapchain resources renderer выбирает capability-approved
|
||||
depth/stencil format, выделяет device-local attachment, очищает его в render
|
||||
pass и прикрепляет к каждому framebuffer. `TestWrite` включает depth test и
|
||||
write; `TestReadOnly` включает test без write. Alpha-test пока завершается
|
||||
явной ошибкой: shader variant ещё не существует, поэтому renderer не должен
|
||||
молча подменять state. Также не установлен источник значений для Batch20/MAT0: их
|
||||
поля нельзя объявлять blend/depth/cull mapping без dynamic capture или
|
||||
дополнительного дизассемблирования. Это частично реализованная compatibility
|
||||
boundary, а не заявление о готовой parity fixed-function state.
|
||||
|
||||
Выбор будущего depth/stencil attachment уже отделён от renderer lifetime:
|
||||
Выбор depth/stencil attachment отделён от renderer lifetime:
|
||||
`select_depth_stencil_attachment_format` применяет тот же фиксированный порядок
|
||||
форматов, что и capability gate, к фактически поддерживаемому списку GPU.
|
||||
Это исключает ситуацию, когда admission принимает один совместимый формат, а
|
||||
allocation позднее выбирает другой; сам attachment и render-pass integration
|
||||
остаются следующей отдельной задачей.
|
||||
allocation позднее выбирает другой; resource уничтожается после framebuffer и
|
||||
render pass при recreate/teardown.
|
||||
|
||||
После последнего world pass renderer закрывает сцену и выводит back buffer.
|
||||
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-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-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-PIPELINE-001 covered-gpu cargo run -p fparkan-vulkan-smoke --release --locked -- --out target/fparkan/stage3-depth-attachment-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; capability-selected device-local depth attachment supports TestWrite/TestReadOnly, baseline key is live-proven; unproven Batch20/MAT0 state mapping and unsupported alpha-test 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-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
|
||||
|
||||
|
Reference in New Issue
Block a user