dreamwell_runtime/
window.rs1use std::sync::Arc;
4use winit::event_loop::ActiveEventLoop;
5use winit::window::Window;
6
7pub 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 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}