fix(vulkan-policy): gate requested depth formats
This commit is contained in:
@@ -38,7 +38,8 @@ mod swapchain_resources;
|
|||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
pub use self::capabilities::{
|
pub use self::capabilities::{
|
||||||
probe_vulkan_runtime_capabilities, VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe,
|
probe_vulkan_runtime_capabilities, probe_vulkan_runtime_capabilities_for_request,
|
||||||
|
VulkanRuntimeCapabilityError, VulkanRuntimeCapabilityProbe,
|
||||||
};
|
};
|
||||||
pub use self::instance::{
|
pub use self::instance::{
|
||||||
create_vulkan_instance_probe, plan_vulkan_instance, probe_vulkan_loader,
|
create_vulkan_instance_probe, plan_vulkan_instance, probe_vulkan_loader,
|
||||||
@@ -54,8 +55,8 @@ use self::resources::{
|
|||||||
VulkanFrameSync,
|
VulkanFrameSync,
|
||||||
};
|
};
|
||||||
pub use self::runtime::{
|
pub use self::runtime::{
|
||||||
create_vulkan_logical_device_probe, VulkanLogicalDeviceError, VulkanLogicalDeviceProbe,
|
create_vulkan_logical_device_probe, create_vulkan_logical_device_probe_for_request,
|
||||||
VulkanLogicalDeviceReport,
|
VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanLogicalDeviceReport,
|
||||||
};
|
};
|
||||||
pub use self::smoke_types::{
|
pub use self::smoke_types::{
|
||||||
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
|
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use fparkan_platform::RenderRequest;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
use super::{VulkanInstanceProbe, VulkanSurfaceProbe};
|
use super::{VulkanInstanceProbe, VulkanSurfaceProbe};
|
||||||
use crate::policy::{
|
use crate::policy::{
|
||||||
compare_reports, plan_vulkan_swapchain, validate_device, VulkanCapabilityError,
|
compare_reports, plan_vulkan_swapchain, validate_device_for_request, VulkanCapabilityError,
|
||||||
VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanQueueFamily,
|
VulkanCapabilityReport, VulkanDeviceType, VulkanPhysicalDeviceRecord, VulkanQueueFamily,
|
||||||
VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSwapchainRequest,
|
VulkanSurfaceFormat, VulkanSwapchainError, VulkanSwapchainPlan, VulkanSwapchainRequest,
|
||||||
VulkanSwapchainSurfaceCapabilities,
|
VulkanSwapchainSurfaceCapabilities,
|
||||||
@@ -133,14 +134,42 @@ pub fn probe_vulkan_runtime_capabilities(
|
|||||||
surface: &VulkanSurfaceProbe,
|
surface: &VulkanSurfaceProbe,
|
||||||
drawable_extent: (u32, u32),
|
drawable_extent: (u32, u32),
|
||||||
) -> Result<VulkanRuntimeCapabilityProbe, VulkanRuntimeCapabilityError> {
|
) -> Result<VulkanRuntimeCapabilityProbe, VulkanRuntimeCapabilityError> {
|
||||||
let selected = select_live_device_candidate(instance, surface, drawable_extent)?;
|
let selected = select_live_device_candidate_for_request(
|
||||||
|
instance,
|
||||||
|
surface,
|
||||||
|
drawable_extent,
|
||||||
|
&RenderRequest::conservative(),
|
||||||
|
)?;
|
||||||
Ok(selected.runtime)
|
Ok(selected.runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn select_live_device_candidate(
|
/// Probes live Vulkan device, queue, surface and swapchain capabilities for a
|
||||||
|
/// specific Stage 0 render request.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`VulkanRuntimeCapabilityError`] when device enumeration, surface
|
||||||
|
/// capability queries, Stage 0 device selection, or swapchain planning fails.
|
||||||
|
pub fn probe_vulkan_runtime_capabilities_for_request(
|
||||||
instance: &VulkanInstanceProbe,
|
instance: &VulkanInstanceProbe,
|
||||||
surface: &VulkanSurfaceProbe,
|
surface: &VulkanSurfaceProbe,
|
||||||
drawable_extent: (u32, u32),
|
drawable_extent: (u32, u32),
|
||||||
|
render_request: &RenderRequest,
|
||||||
|
) -> Result<VulkanRuntimeCapabilityProbe, VulkanRuntimeCapabilityError> {
|
||||||
|
let selected = select_live_device_candidate_for_request(
|
||||||
|
instance,
|
||||||
|
surface,
|
||||||
|
drawable_extent,
|
||||||
|
render_request,
|
||||||
|
)?;
|
||||||
|
Ok(selected.runtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn select_live_device_candidate_for_request(
|
||||||
|
instance: &VulkanInstanceProbe,
|
||||||
|
surface: &VulkanSurfaceProbe,
|
||||||
|
drawable_extent: (u32, u32),
|
||||||
|
render_request: &RenderRequest,
|
||||||
) -> Result<SelectedLiveDevice, VulkanRuntimeCapabilityError> {
|
) -> Result<SelectedLiveDevice, VulkanRuntimeCapabilityError> {
|
||||||
let devices = {
|
let devices = {
|
||||||
// SAFETY: The Vulkan instance is live for this query and no handles are retained.
|
// SAFETY: The Vulkan instance is live for this query and no handles are retained.
|
||||||
@@ -151,13 +180,14 @@ pub(super) fn select_live_device_candidate(
|
|||||||
let mut best: Option<LiveDeviceCandidate> = None;
|
let mut best: Option<LiveDeviceCandidate> = None;
|
||||||
let mut last_error = None;
|
let mut last_error = None;
|
||||||
for (index, device) in devices.iter().copied().enumerate() {
|
for (index, device) in devices.iter().copied().enumerate() {
|
||||||
let candidate = match live_device_candidate(instance, surface, device, index) {
|
let candidate =
|
||||||
Ok(candidate) => candidate,
|
match live_device_candidate(instance, surface, device, index, render_request) {
|
||||||
Err(err) => {
|
Ok(candidate) => candidate,
|
||||||
last_error = Some(err);
|
Err(err) => {
|
||||||
continue;
|
last_error = Some(err);
|
||||||
}
|
continue;
|
||||||
};
|
}
|
||||||
|
};
|
||||||
match &best {
|
match &best {
|
||||||
Some(existing)
|
Some(existing)
|
||||||
if compare_reports(&candidate.capability, &existing.capability)
|
if compare_reports(&candidate.capability, &existing.capability)
|
||||||
@@ -192,6 +222,7 @@ fn live_device_candidate(
|
|||||||
surface: &VulkanSurfaceProbe,
|
surface: &VulkanSurfaceProbe,
|
||||||
device: vk::PhysicalDevice,
|
device: vk::PhysicalDevice,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
render_request: &RenderRequest,
|
||||||
) -> Result<LiveDeviceCandidate, VulkanRuntimeCapabilityError> {
|
) -> Result<LiveDeviceCandidate, VulkanRuntimeCapabilityError> {
|
||||||
let properties = {
|
let properties = {
|
||||||
// SAFETY: `device` was returned by this live instance and the result is copied by value.
|
// SAFETY: `device` was returned by this live instance and the result is copied by value.
|
||||||
@@ -210,6 +241,7 @@ fn live_device_candidate(
|
|||||||
let surface_formats = live_surface_formats(surface, device, &name)?;
|
let surface_formats = live_surface_formats(surface, device, &name)?;
|
||||||
let present_modes = live_present_modes(surface, device, &name)?;
|
let present_modes = live_present_modes(surface, device, &name)?;
|
||||||
let surface_capabilities = live_surface_capabilities(surface, device, &name)?;
|
let surface_capabilities = live_surface_capabilities(surface, device, &name)?;
|
||||||
|
let supported_depth_stencil_formats = live_depth_stencil_formats(instance, device);
|
||||||
let queue_families = queue_properties
|
let queue_families = queue_properties
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -251,8 +283,10 @@ fn live_device_candidate(
|
|||||||
surface_formats: surface_formats.clone(),
|
surface_formats: surface_formats.clone(),
|
||||||
present_modes: present_modes.clone(),
|
present_modes: present_modes.clone(),
|
||||||
surface_capabilities,
|
surface_capabilities,
|
||||||
|
supported_depth_stencil_formats,
|
||||||
};
|
};
|
||||||
let capability = validate_device(&record).map_err(VulkanRuntimeCapabilityError::Capability)?;
|
let capability = validate_device_for_request(&record, render_request)
|
||||||
|
.map_err(VulkanRuntimeCapabilityError::Capability)?;
|
||||||
Ok(LiveDeviceCandidate {
|
Ok(LiveDeviceCandidate {
|
||||||
physical_device: device,
|
physical_device: device,
|
||||||
capability,
|
capability,
|
||||||
@@ -403,3 +437,34 @@ pub(super) fn live_surface_capabilities(
|
|||||||
supported_usage_flags: capabilities.supported_usage_flags.as_raw(),
|
supported_usage_flags: capabilities.supported_usage_flags.as_raw(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn live_depth_stencil_formats(
|
||||||
|
instance: &VulkanInstanceProbe,
|
||||||
|
device: vk::PhysicalDevice,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
[
|
||||||
|
vk::Format::D16_UNORM,
|
||||||
|
vk::Format::X8_D24_UNORM_PACK32,
|
||||||
|
vk::Format::D32_SFLOAT,
|
||||||
|
vk::Format::S8_UINT,
|
||||||
|
vk::Format::D16_UNORM_S8_UINT,
|
||||||
|
vk::Format::D24_UNORM_S8_UINT,
|
||||||
|
vk::Format::D32_SFLOAT_S8_UINT,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|format| {
|
||||||
|
let properties = {
|
||||||
|
// SAFETY: `device` belongs to `instance`; format-property queries copy data by value.
|
||||||
|
unsafe {
|
||||||
|
instance
|
||||||
|
.instance
|
||||||
|
.get_physical_device_format_properties(device, *format)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
properties
|
||||||
|
.optimal_tiling_features
|
||||||
|
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||||
|
})
|
||||||
|
.map(vk::Format::as_raw)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use fparkan_platform::RenderRequest;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
use super::capabilities::{
|
use super::capabilities::{
|
||||||
select_live_device_candidate, unique_queue_families, VulkanRuntimeCapabilityError,
|
select_live_device_candidate_for_request, unique_queue_families, VulkanRuntimeCapabilityError,
|
||||||
VulkanRuntimeCapabilityProbe,
|
VulkanRuntimeCapabilityProbe,
|
||||||
};
|
};
|
||||||
use super::{VulkanInstanceProbe, VulkanSurfaceProbe};
|
use super::{VulkanInstanceProbe, VulkanSurfaceProbe};
|
||||||
@@ -125,8 +126,33 @@ pub fn create_vulkan_logical_device_probe(
|
|||||||
surface: &VulkanSurfaceProbe,
|
surface: &VulkanSurfaceProbe,
|
||||||
drawable_extent: (u32, u32),
|
drawable_extent: (u32, u32),
|
||||||
) -> Result<VulkanLogicalDeviceProbe, VulkanLogicalDeviceError> {
|
) -> Result<VulkanLogicalDeviceProbe, VulkanLogicalDeviceError> {
|
||||||
let selected = select_live_device_candidate(instance, surface, drawable_extent)
|
create_vulkan_logical_device_probe_for_request(
|
||||||
.map_err(VulkanLogicalDeviceError::Runtime)?;
|
instance,
|
||||||
|
surface,
|
||||||
|
drawable_extent,
|
||||||
|
&RenderRequest::conservative(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a Vulkan logical device for a specific Stage 0 render request.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`VulkanLogicalDeviceError`] when runtime capability probing fails,
|
||||||
|
/// device extension names are invalid, or `vkCreateDevice` fails.
|
||||||
|
pub fn create_vulkan_logical_device_probe_for_request(
|
||||||
|
instance: &VulkanInstanceProbe,
|
||||||
|
surface: &VulkanSurfaceProbe,
|
||||||
|
drawable_extent: (u32, u32),
|
||||||
|
render_request: &RenderRequest,
|
||||||
|
) -> Result<VulkanLogicalDeviceProbe, VulkanLogicalDeviceError> {
|
||||||
|
let selected = select_live_device_candidate_for_request(
|
||||||
|
instance,
|
||||||
|
surface,
|
||||||
|
drawable_extent,
|
||||||
|
render_request,
|
||||||
|
)
|
||||||
|
.map_err(VulkanLogicalDeviceError::Runtime)?;
|
||||||
let capability = &selected.runtime.capability;
|
let capability = &selected.runtime.capability;
|
||||||
let queue_priorities = [1.0_f32];
|
let queue_priorities = [1.0_f32];
|
||||||
let queue_families = unique_queue_families(
|
let queue_families = unique_queue_families(
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ use ash::vk;
|
|||||||
use super::{
|
use super::{
|
||||||
create_command_pool, create_frame_sync, create_swapchain_resources,
|
create_command_pool, create_frame_sync, create_swapchain_resources,
|
||||||
create_triangle_index_buffer, create_triangle_vertex_buffer, create_validation_messenger,
|
create_triangle_index_buffer, create_triangle_vertex_buffer, create_validation_messenger,
|
||||||
create_vulkan_instance_probe, create_vulkan_logical_device_probe, create_vulkan_surface_probe,
|
create_vulkan_instance_probe, create_vulkan_logical_device_probe_for_request,
|
||||||
create_vulkan_swapchain_probe_for_extent, destroy_allocated_buffer,
|
create_vulkan_surface_probe, create_vulkan_swapchain_probe_for_extent,
|
||||||
destroy_swapchain_resources, plan_vulkan_surface, VulkanAllocatedBuffer, VulkanInstanceConfig,
|
destroy_allocated_buffer, destroy_swapchain_resources, plan_vulkan_surface,
|
||||||
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
VulkanAllocatedBuffer, VulkanInstanceConfig, VulkanInstanceProbe, VulkanLogicalDeviceProbe,
|
||||||
VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError, VulkanSmokeRendererReport,
|
VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo,
|
||||||
VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainResources, VulkanValidationMessenger,
|
VulkanSmokeRendererError, VulkanSmokeRendererReport, VulkanSurfaceProbe, VulkanSwapchainProbe,
|
||||||
VulkanValidationReport,
|
VulkanSwapchainResources, VulkanValidationMessenger, VulkanValidationReport,
|
||||||
};
|
};
|
||||||
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
|
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
|
||||||
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
|
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
|
||||||
@@ -106,9 +106,13 @@ impl VulkanSmokeRenderer {
|
|||||||
if let Some(progress) = bootstrap_progress {
|
if let Some(progress) = bootstrap_progress {
|
||||||
progress.mark_surface_created();
|
progress.mark_surface_created();
|
||||||
}
|
}
|
||||||
let device =
|
let device = create_vulkan_logical_device_probe_for_request(
|
||||||
create_vulkan_logical_device_probe(&instance, &surface, create_info.drawable_extent)
|
&instance,
|
||||||
.map_err(VulkanSmokeRendererError::LogicalDevice)?;
|
&surface,
|
||||||
|
create_info.drawable_extent,
|
||||||
|
&create_info.render_request,
|
||||||
|
)
|
||||||
|
.map_err(VulkanSmokeRendererError::LogicalDevice)?;
|
||||||
if let Some(progress) = bootstrap_progress {
|
if let Some(progress) = bootstrap_progress {
|
||||||
progress.mark_logical_device_created();
|
progress.mark_logical_device_created();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use ash::vk;
|
use ash::vk;
|
||||||
use fparkan_platform::NativeWindowHandles;
|
use fparkan_platform::{NativeWindowHandles, RenderRequest};
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ pub struct VulkanSmokeRendererCreateInfo {
|
|||||||
pub native_handles: NativeWindowHandles,
|
pub native_handles: NativeWindowHandles,
|
||||||
/// Initial drawable extent.
|
/// Initial drawable extent.
|
||||||
pub drawable_extent: (u32, u32),
|
pub drawable_extent: (u32, u32),
|
||||||
|
/// Stage 0 render request used for capability gating.
|
||||||
|
pub render_request: RenderRequest,
|
||||||
/// Whether validation layers must be enabled.
|
/// Whether validation layers must be enabled.
|
||||||
pub enable_validation: bool,
|
pub enable_validation: bool,
|
||||||
/// Optional shared bootstrap progress tracker for failure evidence.
|
/// Optional shared bootstrap progress tracker for failure evidence.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::shader_manifest::{
|
|||||||
TRIANGLE_VERTEX_SPIRV_PATH, TRIANGLE_VERTEX_VALIDATE_COMMAND,
|
TRIANGLE_VERTEX_SPIRV_PATH, TRIANGLE_VERTEX_VALIDATE_COMMAND,
|
||||||
};
|
};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use fparkan_platform::RenderRequest;
|
use fparkan_platform::{DepthStencilSupport, RenderRequest};
|
||||||
use fparkan_render::{
|
use fparkan_render::{
|
||||||
DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderCommand, RenderPhase,
|
DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderCommand, RenderPhase,
|
||||||
};
|
};
|
||||||
@@ -287,6 +287,36 @@ fn rejects_missing_graphics_present_swapchain_and_format() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn capability_gate_rejects_devices_without_requested_depth_stencil_support() {
|
||||||
|
let mut no_depth = device("No depth", VulkanDeviceType::DiscreteGpu, 0, true, false);
|
||||||
|
no_depth.supported_depth_stencil_formats = vec![vk::Format::D32_SFLOAT.as_raw()];
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
select_physical_device(&[no_depth]),
|
||||||
|
Err(VulkanCapabilityError::MissingDepthStencilFormat { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn capability_gate_respects_request_specific_depth_profiles() {
|
||||||
|
let mut no_stencil = device("No stencil", VulkanDeviceType::DiscreteGpu, 0, true, false);
|
||||||
|
no_stencil.supported_depth_stencil_formats = vec![vk::Format::D32_SFLOAT.as_raw()];
|
||||||
|
let relaxed_request = RenderRequest {
|
||||||
|
depth: DepthStencilSupport {
|
||||||
|
depth_bits: 32,
|
||||||
|
stencil_bits: 0,
|
||||||
|
},
|
||||||
|
..RenderRequest::conservative()
|
||||||
|
};
|
||||||
|
|
||||||
|
let report = select_physical_device_for_request(&[no_stencil], &relaxed_request)
|
||||||
|
.expect("selected device for depth-only request");
|
||||||
|
|
||||||
|
assert_eq!(report.device_name, "No stencil");
|
||||||
|
assert!(report.rejected_devices.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn capability_report_json_is_stable() {
|
fn capability_report_json_is_stable() {
|
||||||
let mut rejected = device("Rejected", VulkanDeviceType::IntegratedGpu, 0, true, false);
|
let mut rejected = device("Rejected", VulkanDeviceType::IntegratedGpu, 0, true, false);
|
||||||
@@ -660,6 +690,11 @@ fn device(
|
|||||||
vk::PresentModeKHR::MAILBOX.as_raw(),
|
vk::PresentModeKHR::MAILBOX.as_raw(),
|
||||||
],
|
],
|
||||||
surface_capabilities: default_surface_capabilities(),
|
surface_capabilities: default_surface_capabilities(),
|
||||||
|
supported_depth_stencil_formats: vec![
|
||||||
|
vk::Format::D24_UNORM_S8_UINT.as_raw(),
|
||||||
|
vk::Format::D32_SFLOAT_S8_UINT.as_raw(),
|
||||||
|
vk::Format::D32_SFLOAT.as_raw(),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use fparkan_platform::{DepthStencilSupport, RenderRequest};
|
||||||
use fparkan_render::{validate_command_list, RenderCommand, RenderCommandList, RenderError};
|
use fparkan_render::{validate_command_list, RenderCommand, RenderCommandList, RenderError};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -182,6 +183,8 @@ pub struct VulkanPhysicalDeviceRecord {
|
|||||||
pub present_modes: Vec<i32>,
|
pub present_modes: Vec<i32>,
|
||||||
/// Surface capabilities accepted by the target surface.
|
/// Surface capabilities accepted by the target surface.
|
||||||
pub surface_capabilities: VulkanSwapchainSurfaceCapabilities,
|
pub surface_capabilities: VulkanSwapchainSurfaceCapabilities,
|
||||||
|
/// Depth/stencil attachment formats supported by the device.
|
||||||
|
pub supported_depth_stencil_formats: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanPhysicalDeviceRecord {
|
impl VulkanPhysicalDeviceRecord {
|
||||||
@@ -270,6 +273,13 @@ pub enum VulkanCapabilityError {
|
|||||||
/// Device name that failed validation.
|
/// Device name that failed validation.
|
||||||
device: String,
|
device: String,
|
||||||
},
|
},
|
||||||
|
/// No compatible depth/stencil attachment format exists for the render request.
|
||||||
|
MissingDepthStencilFormat {
|
||||||
|
/// Device name that failed validation.
|
||||||
|
device: String,
|
||||||
|
/// Requested depth/stencil profile.
|
||||||
|
requested: DepthStencilSupport,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for VulkanCapabilityError {
|
impl std::fmt::Display for VulkanCapabilityError {
|
||||||
@@ -301,6 +311,12 @@ impl std::fmt::Display for VulkanCapabilityError {
|
|||||||
f,
|
f,
|
||||||
"Vulkan device {device} surface does not support COLOR_ATTACHMENT usage"
|
"Vulkan device {device} surface does not support COLOR_ATTACHMENT usage"
|
||||||
),
|
),
|
||||||
|
Self::MissingDepthStencilFormat { device, requested } => write!(
|
||||||
|
f,
|
||||||
|
"Vulkan device {device} lacks a depth/stencil attachment format for {}-bit depth and {}-bit stencil",
|
||||||
|
requested.depth_bits,
|
||||||
|
requested.stencil_bits
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +331,20 @@ impl std::error::Error for VulkanCapabilityError {}
|
|||||||
/// API version, queue, swapchain-extension and surface-format requirements.
|
/// API version, queue, swapchain-extension and surface-format requirements.
|
||||||
pub fn select_physical_device(
|
pub fn select_physical_device(
|
||||||
devices: &[VulkanPhysicalDeviceRecord],
|
devices: &[VulkanPhysicalDeviceRecord],
|
||||||
|
) -> Result<VulkanCapabilityReport, VulkanCapabilityError> {
|
||||||
|
select_physical_device_for_request(devices, &RenderRequest::conservative())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Selects a Vulkan physical device for a specific Stage 0 render request.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`VulkanCapabilityError`] when no candidate satisfies the minimum
|
||||||
|
/// API version, queue, swapchain-extension, surface-format or depth/stencil
|
||||||
|
/// requirements for the requested profile.
|
||||||
|
pub fn select_physical_device_for_request(
|
||||||
|
devices: &[VulkanPhysicalDeviceRecord],
|
||||||
|
render_request: &RenderRequest,
|
||||||
) -> Result<VulkanCapabilityReport, VulkanCapabilityError> {
|
) -> Result<VulkanCapabilityReport, VulkanCapabilityError> {
|
||||||
if devices.is_empty() {
|
if devices.is_empty() {
|
||||||
return Err(VulkanCapabilityError::NoPhysicalDevice);
|
return Err(VulkanCapabilityError::NoPhysicalDevice);
|
||||||
@@ -324,7 +354,7 @@ pub fn select_physical_device(
|
|||||||
let mut rejected_devices = Vec::new();
|
let mut rejected_devices = Vec::new();
|
||||||
let mut last_error = None;
|
let mut last_error = None;
|
||||||
for device in devices {
|
for device in devices {
|
||||||
let report = match validate_device(device) {
|
let report = match validate_device_for_request(device, render_request) {
|
||||||
Ok(report) => report,
|
Ok(report) => report,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
rejected_devices.push(rejected_device_report(device, &err));
|
rejected_devices.push(rejected_device_report(device, &err));
|
||||||
@@ -611,8 +641,9 @@ fn select_image_count(capabilities: VulkanSwapchainSurfaceCapabilities) -> u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn validate_device(
|
pub(crate) fn validate_device_for_request(
|
||||||
device: &VulkanPhysicalDeviceRecord,
|
device: &VulkanPhysicalDeviceRecord,
|
||||||
|
render_request: &RenderRequest,
|
||||||
) -> Result<VulkanCapabilityReport, VulkanCapabilityError> {
|
) -> Result<VulkanCapabilityReport, VulkanCapabilityError> {
|
||||||
if device.api_version < MIN_VULKAN_API_VERSION {
|
if device.api_version < MIN_VULKAN_API_VERSION {
|
||||||
return Err(VulkanCapabilityError::ApiVersionTooLow {
|
return Err(VulkanCapabilityError::ApiVersionTooLow {
|
||||||
@@ -640,6 +671,12 @@ pub(crate) fn validate_device(
|
|||||||
device: device.name.clone(),
|
device: device.name.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if !supports_depth_stencil_request(device, render_request.depth) {
|
||||||
|
return Err(VulkanCapabilityError::MissingDepthStencilFormat {
|
||||||
|
device: device.name.clone(),
|
||||||
|
requested: render_request.depth,
|
||||||
|
});
|
||||||
|
}
|
||||||
let (graphics_queue_family, present_queue_family) = select_queue_families(device)?;
|
let (graphics_queue_family, present_queue_family) = select_queue_families(device)?;
|
||||||
|
|
||||||
let portability_subset = device.supports_extension(KHR_PORTABILITY_SUBSET_EXTENSION);
|
let portability_subset = device.supports_extension(KHR_PORTABILITY_SUBSET_EXTENSION);
|
||||||
@@ -684,6 +721,7 @@ const fn capability_error_code(error: &VulkanCapabilityError) -> &'static str {
|
|||||||
VulkanCapabilityError::MissingColorAttachmentUsage { .. } => {
|
VulkanCapabilityError::MissingColorAttachmentUsage { .. } => {
|
||||||
"missing_color_attachment_usage"
|
"missing_color_attachment_usage"
|
||||||
}
|
}
|
||||||
|
VulkanCapabilityError::MissingDepthStencilFormat { .. } => "missing_depth_stencil_format",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,6 +766,36 @@ fn supports_color_attachment_usage(capabilities: VulkanSwapchainSurfaceCapabilit
|
|||||||
capabilities.supported_usage_flags & vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw() != 0
|
capabilities.supported_usage_flags & vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_depth_stencil_request(
|
||||||
|
device: &VulkanPhysicalDeviceRecord,
|
||||||
|
depth: DepthStencilSupport,
|
||||||
|
) -> bool {
|
||||||
|
if depth.depth_bits == 0 && depth.stencil_bits == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
required_depth_stencil_formats(depth).iter().any(|format| {
|
||||||
|
device
|
||||||
|
.supported_depth_stencil_formats
|
||||||
|
.contains(&format.as_raw())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn required_depth_stencil_formats(depth: DepthStencilSupport) -> &'static [vk::Format] {
|
||||||
|
match (depth.depth_bits, depth.stencil_bits) {
|
||||||
|
(0, 0) => &[],
|
||||||
|
(16, 0) => &[vk::Format::D16_UNORM, vk::Format::D32_SFLOAT],
|
||||||
|
(24, 0) => &[vk::Format::X8_D24_UNORM_PACK32, vk::Format::D32_SFLOAT],
|
||||||
|
(32, 0) => &[vk::Format::D32_SFLOAT],
|
||||||
|
(16, 8) => &[vk::Format::D16_UNORM_S8_UINT, vk::Format::D24_UNORM_S8_UINT],
|
||||||
|
(24, 8) => &[
|
||||||
|
vk::Format::D24_UNORM_S8_UINT,
|
||||||
|
vk::Format::D32_SFLOAT_S8_UINT,
|
||||||
|
],
|
||||||
|
(32, 8) => &[vk::Format::D32_SFLOAT_S8_UINT],
|
||||||
|
_ => &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn score_device(
|
fn score_device(
|
||||||
device: &VulkanPhysicalDeviceRecord,
|
device: &VulkanPhysicalDeviceRecord,
|
||||||
graphics_queue_family: u32,
|
graphics_queue_family: u32,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#![allow(clippy::print_stderr, clippy::print_stdout)]
|
#![allow(clippy::print_stderr, clippy::print_stdout)]
|
||||||
//! Native Vulkan smoke runner entrypoint.
|
//! Native Vulkan smoke runner entrypoint.
|
||||||
|
|
||||||
|
use fparkan_platform::RenderRequest;
|
||||||
use fparkan_platform_winit::{window_native_handles, WinitWindowPlan};
|
use fparkan_platform_winit::{window_native_handles, WinitWindowPlan};
|
||||||
use fparkan_render_vulkan::{
|
use fparkan_render_vulkan::{
|
||||||
VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
||||||
@@ -524,6 +525,7 @@ impl ApplicationHandler for SmokeApp {
|
|||||||
application_name: "fparkan-vulkan-smoke".to_string(),
|
application_name: "fparkan-vulkan-smoke".to_string(),
|
||||||
native_handles,
|
native_handles,
|
||||||
drawable_extent: (size.width.max(1), size.height.max(1)),
|
drawable_extent: (size.width.max(1), size.height.max(1)),
|
||||||
|
render_request: RenderRequest::conservative(),
|
||||||
enable_validation: true,
|
enable_validation: true,
|
||||||
bootstrap_progress: Some(Arc::clone(&self.progress.bootstrap)),
|
bootstrap_progress: Some(Arc::clone(&self.progress.bootstrap)),
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user