Skip to main content

dreamwell_runtime/
window.rs

1// Window + surface creation and resize handling.
2
3use std::sync::Arc;
4use winit::event_loop::ActiveEventLoop;
5use winit::window::Window;
6
7/// Window state — owns the winit window and wgpu surface.
8pub struct WindowState {
9    pub window: Arc<Window>,
10    pub surface: wgpu::Surface<'static>,
11    pub device: wgpu::Device,
12    pub queue: wgpu::Queue,
13    pub surface_config: wgpu::SurfaceConfiguration,
14    pub capabilities: dreamwell_gpu::features::GpuCapabilities,
15}
16
17impl WindowState {
18    pub fn new(event_loop: &ActiveEventLoop, title: &str, width: u32, height: u32) -> Self {
19        let window_attrs = Window::default_attributes()
20            .with_title(title)
21            .with_inner_size(winit::dpi::LogicalSize::new(width, height));
22        let window = Arc::new(
23            event_loop
24                .create_window(window_attrs)
25                .expect("window: failed to create OS window from event loop"),
26        );
27
28        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle(Box::new(
29            event_loop.owned_display_handle(),
30        )));
31
32        let surface = instance
33            .create_surface(window.clone())
34            .expect("surface: failed to create wgpu surface for window");
35
36        let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
37            power_preference: wgpu::PowerPreference::HighPerformance,
38            compatible_surface: Some(&surface),
39            force_fallback_adapter: false,
40        }))
41        .expect("adapter: no suitable GPU adapter found (requires HighPerformance with surface compatibility)");
42
43        let (features, limits, experimental) =
44            dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);
45
46        let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
47            label: Some("dreamwell_runtime"),
48            required_features: features,
49            required_limits: limits,
50            memory_hints: Default::default(),
51            experimental_features: experimental,
52            trace: Default::default(),
53        }))
54        .expect("device: failed to create wgpu device from adapter — check required features and limits");
55
56        // Make wgpu validation errors non-fatal during development.
57        // Log errors instead of panicking so we can see partial rendering output
58        // while debugging buffer alignment issues across the render pipeline.
59        device.set_device_lost_callback(|reason, msg| {
60            log::error!("wgpu device lost: {reason:?} — {msg}");
61        });
62        device.on_uncaptured_error(std::sync::Arc::new(|err| {
63            log::error!("wgpu error (non-fatal): {err}");
64        }));
65
66        let capabilities = dreamwell_gpu::features::GpuCapabilities::from_adapter(&adapter);
67        log::info!("GPU: {}", capabilities.summary());
68
69        let surface_caps = surface.get_capabilities(&adapter);
70        let surface_format = surface_caps
71            .formats
72            .iter()
73            .find(|f| f.is_srgb())
74            .copied()
75            .unwrap_or(surface_caps.formats[0]);
76
77        let size = window.inner_size();
78        let surface_config = wgpu::SurfaceConfiguration {
79            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
80            format: surface_format,
81            width: size.width.max(1),
82            height: size.height.max(1),
83            present_mode: wgpu::PresentMode::AutoVsync,
84            alpha_mode: surface_caps.alpha_modes[0],
85            view_formats: vec![],
86            desired_maximum_frame_latency: 2,
87        };
88        surface.configure(&device, &surface_config);
89
90        log::info!(
91            "Surface: format={:?}, present_mode={:?}, size={}x{}",
92            surface_config.format,
93            surface_config.present_mode,
94            surface_config.width,
95            surface_config.height,
96        );
97
98        Self {
99            window,
100            surface,
101            device,
102            queue,
103            surface_config,
104            capabilities,
105        }
106    }
107
108    pub fn resize(&mut self, width: u32, height: u32) {
109        let max_dim = self.device.limits().max_texture_dimension_2d;
110        let width = width.min(max_dim);
111        let height = height.min(max_dim);
112        if width > 0 && height > 0 {
113            self.surface_config.width = width;
114            self.surface_config.height = height;
115            self.surface.configure(&self.device, &self.surface_config);
116        }
117    }
118
119    pub fn aspect_ratio(&self) -> f32 {
120        self.surface_config.width as f32 / self.surface_config.height.max(1) as f32
121    }
122}