diff options
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi.rs')
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi.rs | 710 |
1 files changed, 3 insertions, 707 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs index cf6901c..e3795a2 100644 --- a/adapters/fparkan-render-vulkan/src/ffi.rs +++ b/adapters/fparkan-render-vulkan/src/ffi.rs @@ -27,13 +27,13 @@ //! //! This crate is the declared low-level Vulkan boundary. +use crate::policy::*; use ash::{ khr::{surface, swapchain}, vk, }; use fparkan_binary::{sha256, sha256_hex}; use fparkan_platform::NativeWindowHandles; -use fparkan_render::{validate_command_list, RenderCommand, RenderCommandList, RenderError}; use serde::Serialize; use std::collections::BTreeSet; use std::ffi::{CStr, CString}; @@ -42,8 +42,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Mutex; /// Minimum Vulkan API version accepted by the Stage 0 backend. pub const MIN_VULKAN_API_VERSION: u32 = vk::API_VERSION_1_1; -const KHR_SWAPCHAIN_EXTENSION: &str = "VK_KHR_swapchain"; -const KHR_PORTABILITY_SUBSET_EXTENSION: &str = "VK_KHR_portability_subset"; const KHR_PORTABILITY_ENUMERATION_EXTENSION: &str = "VK_KHR_portability_enumeration"; const EXT_DEBUG_UTILS_EXTENSION: &str = "VK_EXT_debug_utils"; const VALIDATION_LAYER_NAME: &str = "VK_LAYER_KHRONOS_validation"; @@ -3597,718 +3595,16 @@ fn render_shader_manifest_without_hash_json(modules: &[VulkanShaderModuleReport] } } -/// Synthetic physical-device type used by deterministic capability scoring. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum VulkanDeviceType { - /// Discrete GPU. - DiscreteGpu, - /// Integrated GPU. - IntegratedGpu, - /// CPU or software Vulkan implementation. - Cpu, - /// Other or unknown implementation. - Other, -} - -impl VulkanDeviceType { - const fn score_bonus(self) -> i32 { - match self { - Self::DiscreteGpu => 1_000, - Self::IntegratedGpu => 700, - Self::Cpu => 100, - Self::Other => 10, - } - } -} - -/// Queue-family capabilities needed by the Stage 0 renderer. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct VulkanQueueFamily { - /// Stable queue-family index. - pub index: u32, - /// Whether the family supports graphics commands. - pub graphics: bool, - /// Whether the family supports presentation for the target surface. - pub present: bool, -} - -/// Surface format capability needed by the Stage 0 swapchain policy. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct VulkanSurfaceFormat { - /// Vulkan format numeric value. - pub format: i32, - /// Vulkan color-space numeric value. - pub color_space: i32, -} - -/// Surface capabilities needed by the Stage 0 swapchain policy. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct VulkanSwapchainSurfaceCapabilities { - /// Current surface extent, when dictated by the platform. - pub current_extent: Option<(u32, u32)>, - /// Minimum supported swapchain extent. - pub min_extent: (u32, u32), - /// Maximum supported swapchain extent. - pub max_extent: (u32, u32), - /// Minimum supported image count. - pub min_image_count: u32, - /// Maximum supported image count, or 0 when unbounded. - pub max_image_count: u32, - /// Supported swapchain image-usage flags as raw Vulkan bits. - pub supported_usage_flags: u32, -} - -/// Deterministic swapchain planning input. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanSwapchainRequest { - /// Requested drawable extent. - pub drawable_extent: (u32, u32), - /// Available surface formats. - pub formats: Vec<VulkanSurfaceFormat>, - /// Available present modes as raw Vulkan values. - pub present_modes: Vec<i32>, - /// Surface capabilities. - pub capabilities: VulkanSwapchainSurfaceCapabilities, - /// Preferred present mode. - pub preferred_present_mode: i32, -} - -/// Deterministic swapchain plan. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanSwapchainPlan { - /// Report schema version. - pub schema: u32, - /// Selected swapchain extent. - pub extent: (u32, u32), - /// Selected surface format. - pub format: VulkanSurfaceFormat, - /// Selected present mode raw Vulkan value. - pub present_mode: i32, - /// Selected image count. - pub image_count: u32, -} - -/// Swapchain planning error. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum VulkanSwapchainError { - /// No surface format was available. - MissingSurfaceFormat, - /// No present mode was available. - MissingPresentMode, - /// Requested or current extent is empty. - EmptyExtent, -} - -impl std::fmt::Display for VulkanSwapchainError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MissingSurfaceFormat => write!(f, "Vulkan swapchain has no surface format"), - Self::MissingPresentMode => write!(f, "Vulkan swapchain has no present mode"), - Self::EmptyExtent => write!(f, "Vulkan swapchain extent must be non-zero"), - } - } -} - -impl std::error::Error for VulkanSwapchainError {} - -/// Swapchain recreation reason. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum VulkanSwapchainRecreationReason { - /// Drawable extent changed. - Resize, - /// Vulkan reported `VK_ERROR_OUT_OF_DATE_KHR`. - OutOfDate, - /// Vulkan reported `VK_SUBOPTIMAL_KHR`. - Suboptimal, -} - -/// Deterministic swapchain recreation report. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanSwapchainRecreationReport { - /// Report schema version. - pub schema: u32, - /// Recreation reason. - pub reason: VulkanSwapchainRecreationReason, - /// Previous extent. - pub previous_extent: (u32, u32), - /// Next extent. - pub next_extent: (u32, u32), -} - -/// Deterministic frame submission plan for command buffers and sync objects. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct VulkanFrameSubmissionPlan { - /// Report schema version. - pub schema: u32, - /// Frames allowed in flight. - pub frames_in_flight: u32, - /// Swapchain-backed primary command buffers. - pub command_buffers: u32, - /// Binary semaphores allocated per frame. - pub semaphores_per_frame: u32, - /// Fences allocated per frame. - pub fences_per_frame: u32, - /// Draw commands encoded into the frame. - pub draw_count: u32, - /// Total indexed vertices submitted by draw commands. - pub indexed_vertex_count: u32, -} - -/// Synthetic physical-device capabilities used by negative tests and reports. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanPhysicalDeviceRecord { - /// Human-readable device name. - pub name: String, - /// Reported Vulkan API version. - pub api_version: u32, - /// Device class. - pub device_type: VulkanDeviceType, - /// Supported device-extension names. - pub extensions: Vec<String>, - /// Queue-family capabilities. - pub queue_families: Vec<VulkanQueueFamily>, - /// Surface formats accepted by the target surface. - pub surface_formats: Vec<VulkanSurfaceFormat>, - /// Present modes accepted by the target surface. - pub present_modes: Vec<i32>, - /// Surface capabilities accepted by the target surface. - pub surface_capabilities: VulkanSwapchainSurfaceCapabilities, -} - -impl VulkanPhysicalDeviceRecord { - /// Returns whether the device supports an extension name. - #[must_use] - pub fn supports_extension(&self, extension: &str) -> bool { - self.extensions - .iter() - .any(|candidate| candidate == extension) - } -} - -/// Selected device and queue capability report. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VulkanCapabilityReport { - /// Report schema version. - pub schema: u32, - /// Selected device name. - pub device_name: String, - /// Selected Vulkan API version. - pub vulkan_api_version: u32, - /// Deterministic score used for device selection. - pub score: i32, - /// Graphics queue family index. - pub graphics_queue_family: u32, - /// Present queue family index. - pub present_queue_family: u32, - /// Whether portability subset is enabled for the selected device. - pub portability_subset: bool, - /// Enabled device extensions. - pub enabled_extensions: Vec<String>, -} - -/// Vulkan capability selection error. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum VulkanCapabilityError { - /// No physical devices were available. - NoPhysicalDevice, - /// Device API version is lower than the Stage 0 minimum. - ApiVersionTooLow { - /// Required Vulkan API version. - required: u32, - /// Reported Vulkan API version. - found: u32, - }, - /// Required graphics queue is unavailable. - NoGraphicsQueue { - /// Device name that failed validation. - device: String, - }, - /// Required present queue is unavailable. - NoPresentQueue { - /// Device name that failed validation. - device: String, - }, - /// Swapchain device extension is unavailable. - MissingSwapchainExtension { - /// Device name that failed validation. - device: String, - }, - /// No compatible surface format exists. - MissingSurfaceFormat { - /// Device name that failed validation. - device: String, - }, - /// No present mode is available for the target surface. - MissingPresentMode { - /// Device name that failed validation. - device: String, - }, - /// Swapchain images cannot be used as color attachments. - MissingColorAttachmentUsage { - /// Device name that failed validation. - device: String, - }, -} - -impl std::fmt::Display for VulkanCapabilityError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::NoPhysicalDevice => write!(f, "no Vulkan physical device available"), - Self::ApiVersionTooLow { required, found } => write!( - f, - "Vulkan API version too low: required {}, found {}", - format_api_version(*required), - format_api_version(*found) - ), - Self::NoGraphicsQueue { device } => { - write!(f, "Vulkan device {device} has no graphics queue") - } - Self::NoPresentQueue { device } => { - write!(f, "Vulkan device {device} has no present queue") - } - Self::MissingSwapchainExtension { device } => { - write!(f, "Vulkan device {device} lacks {KHR_SWAPCHAIN_EXTENSION}") - } - Self::MissingSurfaceFormat { device } => { - write!(f, "Vulkan device {device} has no compatible surface format") - } - Self::MissingPresentMode { device } => { - write!(f, "Vulkan device {device} has no supported present mode") - } - Self::MissingColorAttachmentUsage { device } => write!( - f, - "Vulkan device {device} surface does not support COLOR_ATTACHMENT usage" - ), - } - } -} - -impl std::error::Error for VulkanCapabilityError {} - -/// Selects a Vulkan physical device using deterministic Stage 0 policy. -/// -/// # Errors -/// -/// Returns [`VulkanCapabilityError`] when no candidate satisfies the minimum -/// API version, queue, swapchain-extension and surface-format requirements. -pub fn select_physical_device( - devices: &[VulkanPhysicalDeviceRecord], -) -> Result<VulkanCapabilityReport, VulkanCapabilityError> { - if devices.is_empty() { - return Err(VulkanCapabilityError::NoPhysicalDevice); - } - - let mut best = None; - let mut last_error = None; - for device in devices { - let report = match validate_device(device) { - Ok(report) => report, - Err(err) => { - last_error = Some(err); - continue; - } - }; - match &best { - Some(existing) if compare_reports(&report, existing) != std::cmp::Ordering::Greater => { - } - _ => best = Some(report), - } - } - best.ok_or_else(|| last_error.unwrap_or(VulkanCapabilityError::NoPhysicalDevice)) -} - -/// Builds a deterministic swapchain plan from surface capabilities. -/// -/// # Errors -/// -/// Returns [`VulkanSwapchainError`] when formats, present modes or extent are -/// unusable. -pub fn plan_vulkan_swapchain( - request: &VulkanSwapchainRequest, -) -> Result<VulkanSwapchainPlan, VulkanSwapchainError> { - let format = select_surface_format(&request.formats)?; - let present_mode = select_present_mode(&request.present_modes, request.preferred_present_mode)?; - let extent = select_swapchain_extent(request)?; - if extent.0 == 0 || extent.1 == 0 { - return Err(VulkanSwapchainError::EmptyExtent); - } - Ok(VulkanSwapchainPlan { - schema: 1, - extent, - format, - present_mode, - image_count: select_image_count(request.capabilities), - }) -} - -fn select_surface_format( - formats: &[VulkanSurfaceFormat], -) -> Result<VulkanSurfaceFormat, VulkanSwapchainError> { - if let Some(format) = undefined_surface_format_override(formats) { - return Ok(format); - } - formats - .iter() - .copied() - .find(|format| { - format.format == vk::Format::B8G8R8A8_SRGB.as_raw() - && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw() - }) - .or_else(|| formats.first().copied()) - .ok_or(VulkanSwapchainError::MissingSurfaceFormat) -} - -fn undefined_surface_format_override( - formats: &[VulkanSurfaceFormat], -) -> Option<VulkanSurfaceFormat> { - match formats { - [format] if format.format == vk::Format::UNDEFINED.as_raw() => Some(VulkanSurfaceFormat { - format: vk::Format::B8G8R8A8_SRGB.as_raw(), - color_space: format.color_space, - }), - _ => None, - } -} - -fn select_present_mode(present_modes: &[i32], preferred: i32) -> Result<i32, VulkanSwapchainError> { - if present_modes.contains(&preferred) { - Ok(preferred) - } else if present_modes.contains(&vk::PresentModeKHR::FIFO.as_raw()) { - Ok(vk::PresentModeKHR::FIFO.as_raw()) - } else { - present_modes - .first() - .copied() - .ok_or(VulkanSwapchainError::MissingPresentMode) - } -} - -fn select_swapchain_extent( - request: &VulkanSwapchainRequest, -) -> Result<(u32, u32), VulkanSwapchainError> { - if let Some(extent) = request.capabilities.current_extent { - return if extent.0 == 0 || extent.1 == 0 { - Err(VulkanSwapchainError::EmptyExtent) - } else { - Ok(extent) - }; - } - let width = request.drawable_extent.0.clamp( - request.capabilities.min_extent.0, - request.capabilities.max_extent.0, - ); - let height = request.drawable_extent.1.clamp( - request.capabilities.min_extent.1, - request.capabilities.max_extent.1, - ); - Ok((width, height)) -} - -fn select_image_count(capabilities: VulkanSwapchainSurfaceCapabilities) -> u32 { - let requested = capabilities.min_image_count.saturating_add(1).max(2); - if capabilities.max_image_count == 0 { - requested - } else { - requested.min(capabilities.max_image_count) - } -} - -fn select_composite_alpha(supported: vk::CompositeAlphaFlagsKHR) -> vk::CompositeAlphaFlagsKHR { - if supported.contains(vk::CompositeAlphaFlagsKHR::OPAQUE) { - vk::CompositeAlphaFlagsKHR::OPAQUE - } else if supported.contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED) { - vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED - } else if supported.contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED) { - vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED - } else { - vk::CompositeAlphaFlagsKHR::INHERIT - } -} - -/// Builds a deterministic swapchain recreation report. -#[must_use] -pub const fn swapchain_recreation_report( - reason: VulkanSwapchainRecreationReason, - previous_extent: (u32, u32), - next_extent: (u32, u32), -) -> VulkanSwapchainRecreationReport { - VulkanSwapchainRecreationReport { - schema: 1, - reason, - previous_extent, - next_extent, - } -} - -/// Builds a deterministic frame submission plan for a validated command list. -/// -/// Stage 0 keeps this as a pure planning boundary so command-pool, command-buffer -/// and synchronization policy can be tested without requiring a native surface. -/// -/// # Errors -/// -/// Returns [`RenderError`] when the command list has invalid frame framing, -/// ordering, draw ranges, mesh bounds, or non-finite transforms. -pub fn plan_vulkan_frame_submission( - swapchain: &VulkanSwapchainPlan, - commands: &RenderCommandList, -) -> Result<VulkanFrameSubmissionPlan, RenderError> { - validate_command_list(commands)?; - let mut draw_count = 0_u32; - let mut indexed_vertex_count = 0_u32; - for command in &commands.commands { - if let RenderCommand::Draw(draw) = command { - draw_count = draw_count.saturating_add(1); - indexed_vertex_count = indexed_vertex_count.saturating_add(draw.range.count); - } - } - Ok(VulkanFrameSubmissionPlan { - schema: 1, - frames_in_flight: swapchain.image_count.clamp(1, 2), - command_buffers: swapchain.image_count, - semaphores_per_frame: 2, - fences_per_frame: 1, - draw_count, - indexed_vertex_count, - }) -} - -fn validate_device( - device: &VulkanPhysicalDeviceRecord, -) -> Result<VulkanCapabilityReport, VulkanCapabilityError> { - if device.api_version < MIN_VULKAN_API_VERSION { - return Err(VulkanCapabilityError::ApiVersionTooLow { - required: MIN_VULKAN_API_VERSION, - found: device.api_version, - }); - } - if !device.supports_extension(KHR_SWAPCHAIN_EXTENSION) { - return Err(VulkanCapabilityError::MissingSwapchainExtension { - device: device.name.clone(), - }); - } - if !supports_surface_formats(device) { - return Err(VulkanCapabilityError::MissingSurfaceFormat { - device: device.name.clone(), - }); - } - if device.present_modes.is_empty() { - return Err(VulkanCapabilityError::MissingPresentMode { - device: device.name.clone(), - }); - } - if !supports_color_attachment_usage(device.surface_capabilities) { - return Err(VulkanCapabilityError::MissingColorAttachmentUsage { - device: device.name.clone(), - }); - } - let (graphics_queue_family, present_queue_family) = select_queue_families(device)?; - - let portability_subset = device.supports_extension(KHR_PORTABILITY_SUBSET_EXTENSION); - let mut enabled_extensions = vec![KHR_SWAPCHAIN_EXTENSION.to_string()]; - if portability_subset { - enabled_extensions.push(KHR_PORTABILITY_SUBSET_EXTENSION.to_string()); - } - - Ok(VulkanCapabilityReport { - schema: 1, - device_name: device.name.clone(), - vulkan_api_version: device.api_version, - score: score_device(device, graphics_queue_family, present_queue_family), - graphics_queue_family, - present_queue_family, - portability_subset, - enabled_extensions, - }) -} - -fn select_queue_families( - device: &VulkanPhysicalDeviceRecord, -) -> Result<(u32, u32), VulkanCapabilityError> { - if let Some(unified) = device - .queue_families - .iter() - .filter(|family| family.graphics && family.present) - .min_by_key(|family| family.index) - { - return Ok((unified.index, unified.index)); - } - - let graphics_queue_family = device - .queue_families - .iter() - .filter(|family| family.graphics) - .min_by_key(|family| family.index) - .ok_or_else(|| VulkanCapabilityError::NoGraphicsQueue { - device: device.name.clone(), - })? - .index; - let present_queue_family = device - .queue_families - .iter() - .filter(|family| family.present) - .min_by_key(|family| family.index) - .ok_or_else(|| VulkanCapabilityError::NoPresentQueue { - device: device.name.clone(), - })? - .index; - Ok((graphics_queue_family, present_queue_family)) -} - -fn supports_surface_formats(device: &VulkanPhysicalDeviceRecord) -> bool { - !device.surface_formats.is_empty() -} - -fn supports_color_attachment_usage(capabilities: VulkanSwapchainSurfaceCapabilities) -> bool { - capabilities.supported_usage_flags & vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw() != 0 -} - -fn score_device( - device: &VulkanPhysicalDeviceRecord, - graphics_queue_family: u32, - present_queue_family: u32, -) -> i32 { - let unified_queue_bonus = if graphics_queue_family == present_queue_family { - 100 - } else { - 0 - }; - let portability_penalty = if device.supports_extension(KHR_PORTABILITY_SUBSET_EXTENSION) { - -50 - } else { - 0 - }; - device.device_type.score_bonus() - + unified_queue_bonus - + portability_penalty - + i32::try_from(device.surface_formats.len()).unwrap_or(i32::MAX) -} - -fn compare_reports( - left: &VulkanCapabilityReport, - right: &VulkanCapabilityReport, -) -> std::cmp::Ordering { - left.score - .cmp(&right.score) - .then_with(|| right.device_name.cmp(&left.device_name)) -} - -/// Renders a deterministic JSON capability report. -#[must_use] -pub fn render_capability_report_json(report: &VulkanCapabilityReport) -> String { - #[derive(Serialize)] - struct CapabilityReportJson<'a> { - schema: u32, - vulkan_api: String, - device_name: &'a str, - score: i32, - graphics_queue_family: u32, - present_queue_family: u32, - portability_subset: bool, - enabled_extensions: &'a [String], - } - - serialize_json_or_fallback( - &CapabilityReportJson { - schema: report.schema, - vulkan_api: format_api_version(report.vulkan_api_version), - device_name: &report.device_name, - score: report.score, - graphics_queue_family: report.graphics_queue_family, - present_queue_family: report.present_queue_family, - portability_subset: report.portability_subset, - enabled_extensions: &report.enabled_extensions, - }, - "{\"schema\":0,\"vulkan_api\":\"0.0.0\",\"device_name\":\"unknown\",\"score\":0,\"graphics_queue_family\":0,\"present_queue_family\":0,\"portability_subset\":false,\"enabled_extensions\":[]}", - ) -} - -/// Renders a deterministic JSON swapchain plan. -#[must_use] -pub fn render_swapchain_plan_json(plan: &VulkanSwapchainPlan) -> String { - #[derive(Serialize)] - struct SwapchainPlanJson { - schema: u32, - extent: [u32; 2], - format: i32, - color_space: i32, - present_mode: i32, - image_count: u32, - } - - serialize_json_or_fallback( - &SwapchainPlanJson { - schema: plan.schema, - extent: [plan.extent.0, plan.extent.1], - format: plan.format.format, - color_space: plan.format.color_space, - present_mode: plan.present_mode, - image_count: plan.image_count, - }, - "{\"schema\":0,\"extent\":[0,0],\"format\":0,\"color_space\":0,\"present_mode\":0,\"image_count\":0}", - ) -} - -/// Renders a deterministic JSON swapchain recreation report. -#[must_use] -pub fn render_swapchain_recreation_report_json(report: &VulkanSwapchainRecreationReport) -> String { - #[derive(Serialize)] - struct SwapchainRecreationReportJson<'a> { - schema: u32, - reason: &'a str, - previous_extent: [u32; 2], - next_extent: [u32; 2], - } - - serialize_json_or_fallback( - &SwapchainRecreationReportJson { - schema: report.schema, - reason: match report.reason { - VulkanSwapchainRecreationReason::Resize => "resize", - VulkanSwapchainRecreationReason::OutOfDate => "out_of_date", - VulkanSwapchainRecreationReason::Suboptimal => "suboptimal", - }, - previous_extent: [report.previous_extent.0, report.previous_extent.1], - next_extent: [report.next_extent.0, report.next_extent.1], - }, - "{\"schema\":0,\"reason\":\"unknown\",\"previous_extent\":[0,0],\"next_extent\":[0,0]}", - ) -} - -/// Renders a deterministic JSON frame submission plan. -#[must_use] -pub fn render_frame_submission_plan_json(plan: &VulkanFrameSubmissionPlan) -> String { - serialize_json_or_fallback( - plan, - "{\"schema\":0,\"frames_in_flight\":0,\"command_buffers\":0,\"semaphores_per_frame\":0,\"fences_per_frame\":0,\"draw_count\":0,\"indexed_vertex_count\":0}", - ) -} - -fn serialize_json_or_fallback<T: Serialize>(value: &T, fallback: &str) -> String { - match serde_json::to_string(value) { - Ok(json) => json, - Err(_) => fallback.to_string(), - } -} - -fn format_api_version(version: u32) -> String { - format!( - "{}.{}.{}", - vk::api_version_major(version), - vk::api_version_minor(version), - vk::api_version_patch(version) - ) -} - #[cfg(test)] mod tests { use super::*; + use crate::policy::{KHR_PORTABILITY_SUBSET_EXTENSION, KHR_SWAPCHAIN_EXTENSION}; use crate::*; use fparkan_platform::RenderRequest; - use fparkan_render::RenderBackend; use fparkan_render::{ DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, RenderCommand, RenderPhase, }; + use fparkan_render::{RenderBackend, RenderError}; #[test] fn planning_backend_tracks_render_request_and_simulated_present() -> Result<(), RenderError> { |
