diff options
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/smoke.rs | 7 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs | 2 | ||||
| -rw-r--r-- | apps/fparkan-vulkan-smoke/build.rs | 27 | ||||
| -rw-r--r-- | apps/fparkan-vulkan-smoke/src/main.rs | 88 |
4 files changed, 108 insertions, 16 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs index f10cd7c..e11706f 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs @@ -51,9 +51,10 @@ where fn commit(mut self) -> T { self.rollback.take(); - self.value - .take() - .expect("rollback guard must hold a value until commit") + match self.value.take() { + Some(value) => value, + None => unreachable!("rollback guard must hold a value until commit"), + } } } diff --git a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs index 2a22555..41dfc0f 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/swapchain_resources.rs @@ -259,10 +259,12 @@ fn create_pipeline_layout( }) } +#[allow(clippy::cast_precision_loss)] fn extent_component_to_f32(value: u32) -> f32 { value as f32 } +#[allow(clippy::too_many_lines)] fn create_graphics_pipeline( device: &VulkanLogicalDeviceProbe, render_pass: vk::RenderPass, diff --git a/apps/fparkan-vulkan-smoke/build.rs b/apps/fparkan-vulkan-smoke/build.rs index 25e9178..24da219 100644 --- a/apps/fparkan-vulkan-smoke/build.rs +++ b/apps/fparkan-vulkan-smoke/build.rs @@ -19,20 +19,27 @@ fn main() { println!("cargo:rustc-env=FPARKAN_BUILD_RUST_TOOLCHAIN={toolchain}"); } - let workspace_root = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("manifest dir")).join("../.."); - if let Some(git_dir) = git_dir(&workspace_root) { - emit_git_rerun_hints(&git_dir); - } + if let Some(workspace_root) = workspace_root() { + if let Some(git_dir) = git_dir(&workspace_root) { + emit_git_rerun_hints(&git_dir); + } - if let Some(commit_sha) = env_commit_sha().or_else(|| git_head_commit_sha(&workspace_root)) { - println!("cargo:rustc-env=FPARKAN_BUILD_COMMIT_SHA={commit_sha}"); - } - if let Some(git_dirty) = git_dirty(&workspace_root) { - println!("cargo:rustc-env=FPARKAN_BUILD_GIT_DIRTY={git_dirty}"); + if let Some(commit_sha) = env_commit_sha().or_else(|| git_head_commit_sha(&workspace_root)) + { + println!("cargo:rustc-env=FPARKAN_BUILD_COMMIT_SHA={commit_sha}"); + } + if let Some(git_dirty) = git_dirty(&workspace_root) { + println!("cargo:rustc-env=FPARKAN_BUILD_GIT_DIRTY={git_dirty}"); + } } } +fn workspace_root() -> Option<PathBuf> { + env::var_os("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .map(|manifest_dir| manifest_dir.join("../..")) +} + fn env_commit_sha() -> Option<String> { ["GITHUB_SHA", "SOURCE_VERSION", "BUILD_VCS_NUMBER"] .into_iter() diff --git a/apps/fparkan-vulkan-smoke/src/main.rs b/apps/fparkan-vulkan-smoke/src/main.rs index 41fe385..ddd8a22 100644 --- a/apps/fparkan-vulkan-smoke/src/main.rs +++ b/apps/fparkan-vulkan-smoke/src/main.rs @@ -18,11 +18,14 @@ use fparkan_render_vulkan::{ use serde::Serialize; use std::path::PathBuf; use std::process::Command; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::time::{Duration, Instant}; use winit::application::ApplicationHandler; use winit::dpi::PhysicalSize; +use winit::event::StartCause; use winit::event::WindowEvent; -use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowId}; const SCHEMA_VERSION: &str = "fparkan-native-smoke-v1"; @@ -50,13 +53,37 @@ fn main() { fn run(args: &[String]) -> Result<String, String> { let options = SmokeOptions::parse(args)?; let event_loop = EventLoop::new().map_err(|err| format!("winit event loop: {err}"))?; - let mut app = SmokeApp::new(options); + event_loop.set_control_flow(ControlFlow::Poll); + let completed = Arc::new(AtomicBool::new(false)); + spawn_timeout_watchdog(options.clone(), Arc::clone(&completed)); + let mut app = SmokeApp::new(options, completed); if let Err(err) = event_loop.run_app(&mut app) { app.error = Some(format!("winit event loop: {err}")); } app.finish() } +fn spawn_timeout_watchdog(options: SmokeOptions, completed: Arc<AtomicBool>) { + std::thread::spawn(move || { + std::thread::sleep(Duration::from_secs(options.timeout_seconds)); + if completed.load(Ordering::SeqCst) || options.out.exists() { + return; + } + let failure_reason = format!( + "native smoke timed out after {} seconds", + options.timeout_seconds + ); + if let Ok(report) = render_timeout_failure_report(&options, &failure_reason) { + if let Some(parent) = options.out.parent() { + let _ = std::fs::create_dir_all(parent); + } + let _ = std::fs::write(&options.out, report); + } + eprintln!("{failure_reason}"); + std::process::exit(2); + }); +} + #[derive(Clone, Debug, Eq, PartialEq)] struct SmokeOptions { out: PathBuf, @@ -125,6 +152,7 @@ impl SmokeOptions { struct SmokeApp { options: SmokeOptions, + completed: Arc<AtomicBool>, window_id: Option<WindowId>, window: Option<Window>, renderer: Option<VulkanSmokeRenderer>, @@ -138,9 +166,10 @@ struct SmokeApp { } impl SmokeApp { - fn new(options: SmokeOptions) -> Self { + fn new(options: SmokeOptions, completed: Arc<AtomicBool>) -> Self { Self { options, + completed, window_id: None, window: None, renderer: None, @@ -158,6 +187,7 @@ impl SmokeApp { if let Some(output) = self.output.take() { return Ok(output); } + self.completed.store(true, Ordering::SeqCst); let error = self .error .clone() @@ -330,6 +360,7 @@ impl SmokeApp { return; } }; + self.completed.store(true, Ordering::SeqCst); if let Err(err) = self.write_report(&report) { self.error = Some(err); event_loop.exit(); @@ -352,7 +383,57 @@ impl SmokeApp { } } +fn render_timeout_failure_report( + options: &SmokeOptions, + failure_reason: &str, +) -> Result<String, String> { + let smoke_report = SmokeReport { + schema_version: SCHEMA_VERSION, + commit_sha: compiled_commit_sha(), + git_dirty: compiled_git_dirty(), + runner_identity: measured_runner_identity(), + runner_architecture: actual_architecture(), + rust_toolchain: compiled_rust_toolchain(), + target_triple: compiled_target_triple(), + platform: actual_platform(), + status: "failed", + failure_reason: Some(failure_reason), + frames: 0, + resize_count: 0, + swapchain_recreate_count: 0, + validation_warning_count: 0, + validation_error_count: 0, + validation_vuids: &[], + requested_frames: options.frames, + timeout_seconds: options.timeout_seconds, + shader_manifest_hash: "", + vulkan_loader_status: "failed", + vulkan_instance_status: "failed", + window_status: "failed", + vulkan_surface_status: "failed", + vulkan_device_status: "failed", + vulkan_device_name: "", + vulkan_logical_device_status: "failed", + vulkan_logical_device_graphics_queue_family: 0, + vulkan_logical_device_present_queue_family: 0, + vulkan_logical_device_enabled_extension_count: 0, + vulkan_swapchain_status: "failed", + vulkan_swapchain_width: 0, + vulkan_swapchain_height: 0, + vulkan_swapchain_image_count: 0, + vulkan_portability_enumeration: false, + vulkan_portability_subset_enabled: false, + }; + serde_json::to_string_pretty(&smoke_report) + .map(|json| format!("{json}\n")) + .map_err(|err| format!("native smoke report serialization failed: {err}")) +} + impl ApplicationHandler for SmokeApp { + fn new_events(&mut self, event_loop: &ActiveEventLoop, _cause: StartCause) { + let _ = self.abort_if_timed_out(event_loop); + } + fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.abort_if_timed_out(event_loop) { return; @@ -818,6 +899,7 @@ mod tests { resize_frame: DEFAULT_RESIZE_FRAME, timeout_seconds: 7, }, + completed: Arc::new(AtomicBool::new(false)), window_id: None, window: None, renderer: None, |
