1use std::sync::{
2 atomic::{AtomicBool, Ordering},
3 Arc, Mutex,
4};
5use winit::window::Window;
6
7use rogalik_common::{BuiltInShader, EngineError, GraphicsContext, ResourceId, SpriteParams};
8use rogalik_math::vectors::Vector2f;
9
10mod assets;
11mod renderer2d;
12mod structs;
13mod utils;
14
15const MAX_TIME: f32 = 3600.;
16
17static SURFACE_REFRESH: AtomicBool = AtomicBool::new(false);
22
23struct SurfaceState {
24 surface: wgpu::Surface<'static>,
25 device: wgpu::Device,
26 queue: wgpu::Queue,
27 config: wgpu::SurfaceConfiguration,
28}
29
30pub struct WgpuContext {
31 assets: assets::WgpuAssets,
32 current_camera_id: ResourceId,
33 clear_color: wgpu::Color,
34 renderer2d: renderer2d::Renderer2d,
35 rendering_resolution: Option<(u32, u32)>,
36 surface_state: Arc<Mutex<Option<SurfaceState>>>, time: f32,
38}
39impl WgpuContext {
40 pub fn new(asset_store: Arc<Mutex<rogalik_assets::AssetStore>>) -> Self {
41 Self {
42 assets: assets::WgpuAssets::new(asset_store),
43 current_camera_id: ResourceId::default(),
44 clear_color: wgpu::Color::BLACK,
45 renderer2d: renderer2d::Renderer2d::new(),
46 rendering_resolution: None,
47 surface_state: Arc::new(Mutex::new(None)),
48 time: 0.,
49 }
50 }
51 fn get_current_resolutions(&self) -> (u32, u32, u32, u32) {
53 let (w, h) = match self.surface_state.lock() {
54 Ok(s) => match s.as_ref() {
55 Some(s) => (s.config.width, s.config.height),
56 _ => (0, 0),
57 },
58 _ => (0, 0),
59 };
60 let (rw, rh) = match self.rendering_resolution {
61 Some((rw, rh)) => (rw, rh),
62 None => (w, h),
63 };
64 (w, h, rw, rh)
65 }
66 fn resize_renderer(&mut self) {
67 if let Ok(state) = self.surface_state.lock() {
68 if let Some(state) = state.as_ref() {
69 self.renderer2d
70 .resize(state.config.width, state.config.height);
71 }
72 }
73 }
74 fn resize_cameras(&mut self) {
75 let (vw, vh, rw, rh) = self.get_current_resolutions();
76 for camera in self.assets.cameras.iter_mut() {
77 camera.resize_viewport(vw as f32, vh as f32, rw as f32, rh as f32);
78 }
79 }
80 fn post_surface_state(&mut self) {
81 if let Ok(state) = self.surface_state.lock() {
82 if let Some(state) = state.as_ref() {
83 let w = state.config.width;
84 let h = state.config.height;
85 log::debug!("State config dim: {}, {}", w, h);
86
87 let _ = self.assets.create_wgpu_data(
88 w,
89 h,
90 &state.device,
91 &state.queue,
92 &state.config.format,
93 );
94 log::debug!("Asset data created");
95 let _ = self.renderer2d.create_wgpu_data(
96 &self.assets,
97 w,
98 h,
99 &state.device,
100 &state.queue,
101 &state.config.format,
102 );
103 log::debug!("Renderer2d data created");
104 }
105 SURFACE_REFRESH.store(false, Ordering::Relaxed);
106 }
107 self.resize_cameras();
108 self.resize_renderer();
109 }
110}
111impl GraphicsContext for WgpuContext {
112 fn has_context(&self) -> bool {
113 match self.surface_state.lock() {
114 Ok(s) => s.is_some(),
115 _ => false,
116 }
117 }
118 fn create_context(&mut self, window: Arc<Window>) {
119 #[cfg(not(target_arch = "wasm32"))]
120 pollster::block_on(create_surface_state(self.surface_state.clone(), window));
121 #[cfg(target_arch = "wasm32")]
122 wasm_bindgen_futures::spawn_local(create_surface_state(self.surface_state.clone(), window));
123 }
124 fn update_time(&mut self, delta: f32) {
125 self.time += delta;
126 self.time = self.time % MAX_TIME;
127 }
128 fn update_assets(&mut self) {
129 if let Ok(state) = self.surface_state.lock() {
130 if let Some(state) = state.as_ref() {
131 let _ =
132 self.assets
133 .update_assets(&state.device, &state.queue, &state.config.format);
134 }
135 }
136 }
137 fn set_clear_color(&mut self, color: rogalik_common::Color) {
138 self.clear_color = utils::color_to_wgpu(color);
139 self.renderer2d.set_clear_color(self.clear_color);
140 }
141 fn resize(&mut self, width: u32, height: u32) {
142 if width > 0 && height > 0 {
143 if let Ok(mut state) = self.surface_state.lock() {
144 if let Some(state) = state.as_mut() {
145 state.config.width = width;
146 state.config.height = height;
147 state.surface.configure(&state.device, &state.config);
148 let _ = self.renderer2d.create_wgpu_data(
149 &self.assets,
150 width,
151 height,
152 &state.device,
153 &state.queue,
154 &state.config.format,
155 );
156 let _ = self.assets.update_postprocess_wgpu_data(
157 width,
158 height,
159 &state.device,
160 &state.queue,
161 &state.config.format,
162 );
163 }
164 }
165 self.resize_cameras();
166 self.resize_renderer();
167 }
168 }
169 fn render(&mut self) {
170 if SURFACE_REFRESH.load(Ordering::Relaxed) {
171 self.post_surface_state();
172 }
173 if let Ok(state) = self.surface_state.lock() {
174 if let Some(state) = state.as_ref() {
175 let _ = self.renderer2d.render(
176 &self.assets,
177 self.time,
178 &state.surface,
179 &state.device,
180 &state.queue,
181 );
182 }
183 }
184 }
185 fn set_rendering_resolution(&mut self, w: u32, h: u32) {
186 log::debug!("Setting rendering resolution at: {}x{}", w, h);
187 self.rendering_resolution = Some((w, h));
188 if self
189 .renderer2d
190 .set_rendering_resolution(&mut self.assets, w, h)
191 .is_ok()
192 {
193 if let Ok(state) = self.surface_state.lock() {
194 if let Some(state) = state.as_ref() {
195 let _ = self.renderer2d.create_upscale_pass(
196 &self.assets,
197 &state.device,
198 &state.queue,
199 &state.config.format,
200 );
201 }
202 }
203 }
204 self.resize_cameras();
205 }
206 fn load_texture(&mut self, path: &str) -> ResourceId {
207 self.assets.texture_from_path(path)
208 }
209 fn load_material(&mut self, name: &str, params: rogalik_common::MaterialParams) {
210 self.assets.create_material(name, params);
211 }
213 fn load_shader(&mut self, kind: rogalik_common::ShaderKind, path: &str) -> ResourceId {
214 self.assets.create_shader(kind, path)
216 }
217 fn load_font(
218 &mut self,
219 name: &str,
220 path: &str,
221 rows: usize,
222 cols: usize,
223 padding: Option<(f32, f32)>,
224 shader: Option<ResourceId>,
225 ) {
226 self.assets
227 .load_font(name, path, rows, cols, padding, shader);
228 }
229 fn add_post_process(&mut self, name: &str, params: rogalik_common::PostProcessParams) {
230 self.assets.create_post_process(name, params);
231 }
232 fn draw_sprite(
233 &mut self,
234 material: &str,
235 position: Vector2f,
236 z_index: i32,
237 size: Vector2f,
238 params: SpriteParams,
239 ) -> Result<(), EngineError> {
240 self.renderer2d.draw_atlas_sprite(
241 &self.assets,
242 0,
243 material,
244 self.current_camera_id,
245 position,
246 z_index,
247 size,
248 params,
249 )
250 }
251 fn draw_atlas_sprite(
252 &mut self,
253 atlas: &str,
254 index: usize,
255 position: rogalik_math::vectors::Vector2f,
256 z_index: i32,
257 size: rogalik_math::vectors::Vector2f,
258 params: SpriteParams,
259 ) -> Result<(), EngineError> {
260 self.renderer2d.draw_atlas_sprite(
261 &self.assets,
262 index,
263 atlas,
264 self.current_camera_id,
265 position,
266 z_index,
267 size,
268 params,
269 )
270 }
271 fn draw_text(
272 &mut self,
273 font: &str,
274 text: &str,
275 position: Vector2f,
276 z_index: i32,
277 size: f32,
278 params: SpriteParams,
279 ) -> Result<(), EngineError> {
280 self.renderer2d.draw_text(
281 &self.assets,
282 font,
283 text,
284 self.current_camera_id,
285 position,
286 z_index,
287 size,
288 params,
289 )
290 }
291 fn draw_mesh(
292 &mut self,
293 material: &str,
294 vertices: &[Vector2f],
295 uvs: &[Vector2f],
296 indices: &[u16],
297 z_index: i32,
298 ) -> Result<(), EngineError> {
299 let vs = vertices
300 .iter()
301 .zip(uvs)
302 .map(|(v, uv)| crate::structs::Vertex {
303 position: [v.x, v.y, 0.],
304 color: [1., 1., 1., 1.],
305 tex_coords: [uv.x, uv.y],
306 })
307 .collect::<Vec<_>>();
309 self.renderer2d.draw_mesh(
310 &self.assets,
311 material,
312 self.current_camera_id,
313 &vs,
314 indices,
315 z_index,
316 )
317 }
318 fn set_ambient(&mut self, color: rogalik_common::Color) {
319 self.renderer2d.set_ambient(color);
320 }
321 fn set_postprocess_strength(&mut self, name: &str, value: f32) -> Result<(), EngineError> {
322 let id = *self
323 .assets
324 .get_postprocess_id(name)
325 .ok_or(EngineError::ResourceNotFound)?;
326 let pass = self
327 .assets
328 .get_postprocess_mut(id)
329 .ok_or(EngineError::ResourceNotFound)?;
330
331 pass.set_strength(value);
332
333 if let Ok(state) = self.surface_state.lock() {
334 if let Some(state) = state.as_ref() {
335 pass.write_buffer(&state.queue)?;
336 };
337 }
338 Ok(())
339 }
340 fn add_light(
341 &mut self,
342 strength: f32,
343 color: rogalik_common::Color,
344 position: Vector2f,
345 ) -> Result<(), EngineError> {
346 self.renderer2d.add_light(strength, color, position)
347 }
348 fn text_dimensions(&self, font: &str, text: &str, size: f32) -> Vector2f {
349 self.assets
350 .get_text_dimensions(font, text, size)
351 .unwrap_or(Vector2f::ZERO)
352 }
353 fn create_camera(&mut self, scale: f32, target: Vector2f) -> ResourceId {
354 let (vw, vh, rw, rh) = self.get_current_resolutions();
355 self.assets
356 .create_camera(vw as f32, vh as f32, rw as f32, rh as f32, scale, target)
357 }
358 fn set_camera(&mut self, id: ResourceId) {
359 self.current_camera_id = id;
360 }
361 fn get_current_camera(&self) -> &dyn rogalik_common::Camera {
362 self.assets.get_camera(self.current_camera_id).unwrap()
363 }
364 fn get_current_camera_mut(&mut self) -> &mut dyn rogalik_common::Camera {
365 self.assets.get_camera_mut(self.current_camera_id).unwrap()
366 }
367 fn get_camera(&self, id: ResourceId) -> Option<&dyn rogalik_common::Camera> {
368 Some(self.assets.get_camera(id)?)
369 }
370 fn get_camera_mut(&mut self, id: ResourceId) -> Option<&mut dyn rogalik_common::Camera> {
371 Some(self.assets.get_camera_mut(id)?)
372 }
373 fn get_builtin_shader(&self, shader: BuiltInShader) -> Option<ResourceId> {
374 self.assets.builtin_shaders.get(&shader).copied()
375 }
376}
377
378async fn create_surface_state(
379 surface_state: Arc<Mutex<Option<SurfaceState>>>,
380 window: Arc<Window>,
381) {
382 log::debug!("Creating WGPU instance");
383 let size = (window.inner_size().width, window.inner_size().height);
384
385 if size.0 == 0 || size.1 == 0 {
386 return;
387 }
388
389 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
390 backends: get_backends(),
392 ..Default::default()
393 });
394 log::debug!("Creating WGPU surface");
395 let surface = instance.create_surface(window).unwrap();
396 log::debug!("Creating WGPU adapter");
397
398 let adapter = instance
399 .request_adapter(&wgpu::RequestAdapterOptions {
400 power_preference: wgpu::PowerPreference::default(),
401 compatible_surface: Some(&surface),
402 force_fallback_adapter: false,
403 })
404 .await
405 .expect("Request for adapter failed!");
406
407 log::debug!("Creating WGPU device");
408
409 let (device, queue) = adapter
410 .request_device(
411 &wgpu::DeviceDescriptor {
412 required_features: wgpu::Features::empty(),
413 required_limits: get_limits(),
414 label: None,
415 memory_hints: Default::default(),
416 },
417 None,
418 )
419 .await
420 .expect("Could not create the device!");
421
422 log::debug!("Config WGPU surface");
423 let surface_caps = surface.get_capabilities(&adapter);
424 log::debug!("WGPU surface capabilities: {:?}", surface_caps);
425 let surface_format = surface_caps
426 .formats
427 .iter()
428 .copied()
429 .find(|f| f.is_srgb())
430 .unwrap_or(surface_caps.formats[0]);
431 log::debug!("WGPU surface format: {:?}", surface_format);
432
433 let present_mode = if surface_caps
434 .present_modes
435 .contains(&wgpu::PresentMode::Fifo)
436 || surface_caps
437 .present_modes
438 .contains(&wgpu::PresentMode::FifoRelaxed)
439 {
440 wgpu::PresentMode::AutoVsync
441 } else {
442 surface_caps.present_modes[0]
443 };
444 log::debug!("WGPU present mode: {:?}", present_mode);
445
446 let config = wgpu::SurfaceConfiguration {
447 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
448 format: surface_format,
449 width: size.0,
450 height: size.1,
451 present_mode,
452 alpha_mode: surface_caps.alpha_modes[0],
453 view_formats: vec![],
454 desired_maximum_frame_latency: 2,
455 };
456 log::debug!("WGPU surface config: {:?}", config);
457 surface.configure(&device, &config);
458 log::debug!("WGPU surface configured");
459
460 if let Ok(mut state) = surface_state.lock() {
461 *state = Some(SurfaceState {
462 surface,
463 device,
464 queue,
465 config,
466 });
467 };
468 SURFACE_REFRESH.store(true, Ordering::Relaxed);
469}
470
471#[cfg(not(target_arch = "wasm32"))]
472fn get_backends() -> wgpu::Backends {
473 wgpu::Backends::all()
474}
475#[cfg(target_arch = "wasm32")]
476fn get_backends() -> wgpu::Backends {
477 wgpu::Backends::GL
478}
479
480#[cfg(not(target_arch = "wasm32"))]
481fn get_limits() -> wgpu::Limits {
482 wgpu::Limits::default()
483}
484#[cfg(target_arch = "wasm32")]
485fn get_limits() -> wgpu::Limits {
486 let mut limits = wgpu::Limits::downlevel_webgl2_defaults();
487 limits.max_color_attachments = 4;
488 limits
489}