1use limnus_app::prelude::{App, Plugin};
6use limnus_default_stages::RenderFirst;
7use limnus_local_resource::prelude::LocalResource;
8use limnus_screen::WindowMessage;
9use limnus_system_params::{LoReM, Msg};
10use std::default::Default;
11use std::sync::Arc;
12use tracing::{debug, info, trace};
13use wgpu::{
14 Adapter, Backends, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor,
15 InstanceFlags, Limits, MemoryHints, Queue, RequestAdapterOptions, RequestDeviceError, Surface,
16 SurfaceConfiguration, SurfaceError,
17};
18use winit::dpi::PhysicalSize;
19use winit::window::Window;
20
21#[derive(Debug, LocalResource)]
22pub struct WgpuWindow {
23 surface: Arc<Surface<'static>>,
24 device: Arc<Device>,
25 queue: Arc<Queue>,
26
27 config: SurfaceConfiguration,
28}
29
30impl WgpuWindow {
31 #[must_use]
32 pub const fn queue(&self) -> &Arc<Queue> {
33 &self.queue
34 }
35}
36
37pub struct ReceiveAnnoyingAsync {
38 pub device_info: Option<BasicDeviceInfo>,
39}
40
41#[derive(Debug, LocalResource)]
42pub struct BasicDeviceInfo {
43 pub adapter: Adapter,
44 pub device: Arc<Device>,
45 pub queue: Arc<Queue>,
46 pub surface: Arc<Surface<'static>>,
47 pub physical_surface_size: PhysicalSize<u32>,
48}
49
50pub async fn annoying_async_device_creation(
51 window: Arc<Window>,
52) -> Result<BasicDeviceInfo, RequestDeviceError> {
53 let instance = Instance::new(InstanceDescriptor {
54 flags: InstanceFlags::advanced_debugging(),
55 dx12_shader_compiler: Default::default(),
56 #[cfg(not(target_arch = "wasm32"))]
57 backends: Backends::PRIMARY,
58 #[cfg(target_arch = "wasm32")]
59 backends: Backends::GL, gles_minor_version: Default::default(),
61 });
62 trace!(?instance, "found instance");
63
64 let surface = instance.create_surface(Arc::clone(&window)).unwrap();
65 trace!(?surface, "surface");
66
67 let adapter = instance
68 .request_adapter(&RequestAdapterOptions {
69 power_preference: Default::default(),
70 compatible_surface: Some(&surface),
71 force_fallback_adapter: false,
72 })
73 .await
74 .unwrap();
75
76 trace!(?adapter, "found adapter");
77
78 let device_descriptor = DeviceDescriptor {
79 label: None,
80 required_features: Features::empty(), required_limits: if cfg!(target_arch = "wasm32") {
82 Limits::downlevel_webgl2_defaults() } else {
84 Limits::default()
85 },
86 memory_hints: MemoryHints::default(), };
88
89 info!(?device_descriptor, "device descriptor");
90
91 let (device, queue) = adapter
92 .request_device(&device_descriptor, None)
93 .await
94 .expect("Failed to request device");
95 info!(?device, "device");
96
97 let inner_size = window.inner_size();
98
99 info!(?inner_size, "inner size");
100
101 Ok(BasicDeviceInfo {
102 adapter,
103 device: device.into(),
104 queue: queue.into(),
105 surface: surface.into(),
106 physical_surface_size: inner_size,
107 })
108}
109
110fn tick(mut wgpu_window: LoReM<WgpuWindow>, window_messages: Msg<WindowMessage>) {
111 for msg in window_messages.iter_previous() {
112 if let WindowMessage::Resized(size) = msg {
113 debug!("resized to {:?}", size);
114 wgpu_window.resize((size.x, size.y));
115 }
116 }
117}
118
119pub struct WgpuWindowPlugin;
120impl Plugin for WgpuWindowPlugin {
121 fn build(&self, _app: &mut App) {}
122
123 fn post_initialization(&self, app: &mut App) {
124 app.insert_local_resource(WgpuWindow::new(
125 app.local_resources().fetch::<BasicDeviceInfo>(),
126 ));
127 app.add_system(RenderFirst, tick);
128 info!("wgpu window plugin is done");
129 }
130}
131
132impl WgpuWindow {
133 #[must_use]
134 pub fn new(info: &BasicDeviceInfo) -> Self {
135 let config = Self::configure_render_surface(info);
136
137 Self {
138 device: Arc::clone(&info.device),
139 config,
140 queue: Arc::clone(&info.queue),
141 surface: Arc::clone(&info.surface),
142 }
143 }
144
145 #[must_use]
146 pub const fn device(&self) -> &Arc<Device> {
147 &self.device
148 }
149
150 fn configure_render_surface(info: &BasicDeviceInfo) -> SurfaceConfiguration {
151 let surface_caps = info.surface.get_capabilities(&info.adapter);
152 let surface_format = surface_caps
153 .formats
154 .iter()
155 .copied()
156 .find(wgpu::TextureFormat::is_srgb)
157 .unwrap_or(surface_caps.formats[0]);
158
159 let config = wgpu::SurfaceConfiguration {
160 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
161 format: surface_format,
162 width: info.physical_surface_size.width,
163 height: info.physical_surface_size.height,
164 present_mode: surface_caps.present_modes[0],
165 alpha_mode: surface_caps.alpha_modes[0],
166 desired_maximum_frame_latency: 2,
167 view_formats: vec![],
168 };
169
170 info.surface.configure(&info.device, &config);
171
172 let present_mode = surface_caps.present_modes[0];
173 let alpha_mode = surface_caps.alpha_modes[0];
174 trace!(
175 "found surface format {:?} {:?} {:?}",
176 surface_format, present_mode, alpha_mode
177 );
178
179 config
180 }
181
182 #[must_use]
183 pub const fn texture_format(&self) -> wgpu::TextureFormat {
184 self.config.format
185 }
186
187 pub fn resize(&mut self, new_size: (u16, u16)) {
188 let width = new_size.0 as usize;
189 let height = new_size.1 as usize;
190
191 if width == 0 || height == 0 {
192 return;
193 }
194
195 self.config.width = width as u32;
196 self.config.height = height as u32;
197 self.surface.configure(&self.device, &self.config);
198 }
199
200 pub fn render(
201 &self,
202 mut render_fn: impl FnMut(&mut wgpu::CommandEncoder, &wgpu::TextureView),
203 ) -> Result<(), SurfaceError> {
204 let surface_texture = self.surface.get_current_texture()?;
206
207 let texture_view = surface_texture
208 .texture
209 .create_view(&wgpu::TextureViewDescriptor::default());
210
211 let mut encoder = self
212 .device
213 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
214 label: Some("Render Encoder"),
215 });
216
217 render_fn(&mut encoder, &texture_view);
218
219 self.queue.submit(std::iter::once(encoder.finish()));
220
221 surface_texture.present();
222
223 Ok(())
224 }
225}