feat(stage0): close native smoke acceptance gate
This commit is contained in:
@@ -27,15 +27,14 @@ use fparkan_platform::{
|
||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::PhysicalSize as WinitPhysicalSize;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Instant;
|
||||
use winit::event::{Event, MouseButton, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::scancode::PhysicalKeyExtScancode;
|
||||
use winit::window::{Window, WindowId};
|
||||
use winit::window::Window;
|
||||
|
||||
static NEXT_WINDOW_HANDLE_ID: AtomicU64 = AtomicU64::new(1);
|
||||
static CLOCK_START: OnceLock<Instant> = OnceLock::new();
|
||||
const DEFAULT_SMOKE_WIDTH: u32 = 1280;
|
||||
const DEFAULT_SMOKE_HEIGHT: u32 = 720;
|
||||
|
||||
@@ -49,10 +48,8 @@ pub struct WinitClock;
|
||||
|
||||
impl MonotonicClock for WinitClock {
|
||||
fn now(&self) -> MonotonicInstant {
|
||||
let duration = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
MonotonicInstant(duration.as_millis().try_into().unwrap_or(u64::MAX))
|
||||
let elapsed = CLOCK_START.get_or_init(Instant::now).elapsed();
|
||||
MonotonicInstant(elapsed.as_millis().try_into().unwrap_or(u64::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,113 +184,6 @@ impl WinitWindowPlan {
|
||||
}
|
||||
}
|
||||
|
||||
/// Native smoke window creation result.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct WinitSmokeWindowProbe {
|
||||
/// Validated creation plan.
|
||||
pub plan: WinitWindowPlan,
|
||||
/// Captured window descriptor.
|
||||
pub window: WinitWindow,
|
||||
}
|
||||
|
||||
impl WinitSmokeWindowProbe {
|
||||
/// Returns raw native handles captured from the native window.
|
||||
#[must_use]
|
||||
pub fn native_handles(&self) -> Option<NativeWindowHandles> {
|
||||
self.window.native_handles()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a native smoke window, captures raw handles, then exits the event loop.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`PlatformError`] when the plan is invalid, the event loop/window
|
||||
/// cannot be created, or raw native handles are unavailable.
|
||||
pub fn probe_smoke_window() -> Result<WinitSmokeWindowProbe, PlatformError> {
|
||||
let plan = WinitWindowPlan::smoke().validate()?;
|
||||
let event_loop = EventLoop::new().map_err(|err| PlatformError::Backend {
|
||||
context: "winit event loop",
|
||||
message: err.to_string(),
|
||||
})?;
|
||||
let mut app = SmokeWindowApp::new(plan);
|
||||
event_loop
|
||||
.run_app(&mut app)
|
||||
.map_err(|err| PlatformError::Backend {
|
||||
context: "winit event loop",
|
||||
message: err.to_string(),
|
||||
})?;
|
||||
app.into_probe()
|
||||
}
|
||||
|
||||
struct SmokeWindowApp {
|
||||
plan: WinitWindowPlan,
|
||||
window: Option<WinitWindow>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl SmokeWindowApp {
|
||||
const fn new(plan: WinitWindowPlan) -> Self {
|
||||
Self {
|
||||
plan,
|
||||
window: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_probe(self) -> Result<WinitSmokeWindowProbe, PlatformError> {
|
||||
if let Some(message) = self.error {
|
||||
return Err(PlatformError::Backend {
|
||||
context: "winit smoke window",
|
||||
message,
|
||||
});
|
||||
}
|
||||
let window = self.window.ok_or_else(|| PlatformError::Backend {
|
||||
context: "winit smoke window",
|
||||
message: "event loop exited before creating a window".to_string(),
|
||||
})?;
|
||||
if self.plan.requires_native_handles && window.native_handles().is_none() {
|
||||
return Err(PlatformError::Backend {
|
||||
context: "winit smoke window",
|
||||
message: "native window/display handles are unavailable".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(WinitSmokeWindowProbe {
|
||||
plan: self.plan,
|
||||
window,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for SmokeWindowApp {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.window.is_some() || self.error.is_some() {
|
||||
event_loop.exit();
|
||||
return;
|
||||
}
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("FParkan Vulkan smoke")
|
||||
.with_inner_size(WinitPhysicalSize::new(self.plan.width, self.plan.height));
|
||||
match event_loop.create_window(attributes) {
|
||||
Ok(window) => {
|
||||
self.window = Some(WinitWindow::from_window(&window));
|
||||
}
|
||||
Err(err) => {
|
||||
self.error = Some(err.to_string());
|
||||
}
|
||||
}
|
||||
event_loop.exit();
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
_event: WindowEvent,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal window view over a `winit` window.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct WinitWindow {
|
||||
@@ -323,7 +213,7 @@ impl WinitWindow {
|
||||
focused: true,
|
||||
minimized: false,
|
||||
occluded: false,
|
||||
native_handles: native_handles(window),
|
||||
native_handles: window_native_handles(window),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +274,9 @@ impl WindowPort for WinitWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn native_handles(window: &Window) -> Option<NativeWindowHandles> {
|
||||
/// Extracts raw handles from a live `winit::Window`.
|
||||
#[must_use]
|
||||
pub fn window_native_handles(window: &Window) -> Option<NativeWindowHandles> {
|
||||
let display = window.display_handle().ok()?.as_raw();
|
||||
let window = window.window_handle().ok()?.as_raw();
|
||||
Some(NativeWindowHandles { display, window })
|
||||
@@ -454,33 +346,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_window_app_requires_created_native_window() {
|
||||
let app = SmokeWindowApp::new(WinitWindowPlan::smoke());
|
||||
fn monotonic_clock_uses_process_local_epoch() {
|
||||
let clock = WinitClock;
|
||||
let first = clock.now();
|
||||
let second = clock.now();
|
||||
|
||||
assert!(matches!(
|
||||
app.into_probe(),
|
||||
Err(PlatformError::Backend {
|
||||
context: "winit smoke window",
|
||||
..
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_window_app_rejects_synthetic_window_without_native_handles() {
|
||||
let mut app = SmokeWindowApp::new(WinitWindowPlan::smoke());
|
||||
app.window = Some(WinitWindow::synthetic(
|
||||
DEFAULT_SMOKE_WIDTH,
|
||||
DEFAULT_SMOKE_HEIGHT,
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
app.into_probe(),
|
||||
Err(PlatformError::Backend {
|
||||
context: "winit smoke window",
|
||||
..
|
||||
})
|
||||
));
|
||||
assert!(second >= first);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 in_color;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main() {
|
||||
out_color = vec4(in_color, 1.0);
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 in_position;
|
||||
layout(location = 1) in vec3 in_color;
|
||||
|
||||
layout(location = 0) out vec3 out_color;
|
||||
|
||||
void main() {
|
||||
out_color = in_color;
|
||||
gl_Position = vec4(in_position, 0.0, 1.0);
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user