1use astrelis_core::{
45 geometry::{LogicalSize, PhysicalPosition, PhysicalSize, ScaleFactor},
46 profiling::profile_function,
47};
48use astrelis_winit::{
49 WindowId,
50 window::{Window, WindowBackend},
51};
52use std::sync::Arc;
53
54use crate::{
55 context::{GraphicsContext, GraphicsError},
56 frame::{FrameContext, FrameStats, Surface},
57 gpu_profiling::GpuFrameProfiler,
58};
59
60#[derive(Debug, Clone, Copy)]
65pub struct Viewport {
66 pub position: PhysicalPosition<f32>,
68 pub size: PhysicalSize<f32>,
70 pub scale_factor: ScaleFactor,
72}
73
74impl Default for Viewport {
75 fn default() -> Self {
76 Self {
77 position: PhysicalPosition::new(0.0, 0.0),
78 size: PhysicalSize::new(800.0, 600.0),
79 scale_factor: ScaleFactor(1.0),
81 }
82 }
83}
84
85impl Viewport {
86 pub fn new(width: f32, height: f32, scale_factor: ScaleFactor) -> Self {
88 Self {
89 position: PhysicalPosition::new(0.0, 0.0),
90 size: PhysicalSize::new(width, height),
91 scale_factor,
92 }
93 }
94
95 pub fn from_physical_size(size: PhysicalSize<u32>, scale_factor: ScaleFactor) -> Self {
97 Self {
98 position: PhysicalPosition::new(0.0, 0.0),
99 size: PhysicalSize::new(size.width as f32, size.height as f32),
100 scale_factor,
101 }
102 }
103
104 pub fn is_valid(&self) -> bool {
106 self.size.width > 0.0 && self.size.height > 0.0 && self.scale_factor.0 > 0.0
107 }
108
109 pub fn to_logical(&self) -> LogicalSize<f32> {
111 self.size.to_logical(self.scale_factor)
112 }
113
114 pub fn width(&self) -> f32 {
116 self.size.width
117 }
118
119 pub fn height(&self) -> f32 {
121 self.size.height
122 }
123
124 pub fn x(&self) -> f32 {
126 self.position.x
127 }
128
129 pub fn y(&self) -> f32 {
131 self.position.y
132 }
133}
134
135#[derive(Default)]
137pub struct WindowContextDescriptor {
138 pub format: Option<wgpu::TextureFormat>,
140 pub present_mode: Option<wgpu::PresentMode>,
142 pub alpha_mode: Option<wgpu::CompositeAlphaMode>,
144}
145
146pub(crate) struct PendingReconfigure {
147 pub(crate) resize: Option<PhysicalSize<u32>>,
148}
149
150impl PendingReconfigure {
151 const fn new() -> Self {
152 Self { resize: None }
153 }
154}
155
156pub struct WindowContext {
162 pub(crate) window: Window,
163 pub(crate) context: Arc<GraphicsContext>,
164 pub(crate) surface: wgpu::Surface<'static>,
165 pub(crate) config: wgpu::SurfaceConfiguration,
166 pub(crate) reconfigure: PendingReconfigure,
167}
168
169impl WindowContext {
170 pub fn new(
171 window: Window,
172 context: Arc<GraphicsContext>,
173 descriptor: WindowContextDescriptor,
174 ) -> Result<Self, GraphicsError> {
175 profile_function!();
176 let scale_factor = window.scale_factor();
177 let logical_size = window.logical_size();
178 let physical_size = logical_size.to_physical(scale_factor);
179
180 let surface = context
181 .instance()
182 .create_surface(window.window.clone())
183 .map_err(|e| GraphicsError::SurfaceCreationFailed(e.to_string()))?;
184
185 let mut config = surface
186 .get_default_config(context.adapter(), physical_size.width, physical_size.height)
187 .ok_or_else(|| GraphicsError::SurfaceConfigurationFailed(
188 "No suitable surface configuration found".to_string()
189 ))?;
190
191 if let Some(format) = descriptor.format {
192 config.format = format;
193 }
194 if let Some(present_mode) = descriptor.present_mode {
195 config.present_mode = present_mode;
196 }
197 if let Some(alpha_mode) = descriptor.alpha_mode {
198 config.alpha_mode = alpha_mode;
199 }
200
201 surface.configure(context.device(), &config);
202
203 Ok(Self {
204 window,
205 surface,
206 config,
207 reconfigure: PendingReconfigure::new(),
208 context,
209 })
210 }
211
212 pub fn resized(&mut self, new_size: LogicalSize<u32>) {
214 let scale_factor = self.window.scale_factor();
215 let physical_size = new_size.to_physical(scale_factor);
216 self.reconfigure.resize = Some(physical_size);
217 }
218
219 pub fn resized_physical(&mut self, new_size: PhysicalSize<u32>) {
221 self.reconfigure.resize = Some(new_size);
222 }
223
224 pub fn window(&self) -> &Window {
225 &self.window
226 }
227
228 pub fn graphics_context(&self) -> &GraphicsContext {
229 &self.context
230 }
231
232 pub fn surface(&self) -> &wgpu::Surface<'static> {
233 &self.surface
234 }
235
236 pub fn surface_config(&self) -> &wgpu::SurfaceConfiguration {
237 &self.config
238 }
239
240 pub fn surface_format(&self) -> wgpu::TextureFormat {
246 self.config.format
247 }
248
249 pub fn logical_size(&self) -> LogicalSize<u32> {
251 self.window.logical_size()
252 }
253
254 pub fn physical_size(&self) -> PhysicalSize<u32> {
256 self.window.physical_size()
257 }
258
259 pub fn logical_size_f32(&self) -> LogicalSize<f32> {
261 let size = self.logical_size();
262 LogicalSize::new(size.width as f32, size.height as f32)
263 }
264
265 pub fn physical_size_f32(&self) -> PhysicalSize<f32> {
267 let size = self.physical_size();
268 PhysicalSize::new(size.width as f32, size.height as f32)
269 }
270
271 pub fn reconfigure_surface(&mut self, config: wgpu::SurfaceConfiguration) {
273 self.config = config;
274 self.surface.configure(self.context.device(), &self.config);
275 }
276}
277
278impl WindowContext {
279 fn try_acquire_surface_texture(&mut self) -> Result<wgpu::SurfaceTexture, GraphicsError> {
284 match self.surface.get_current_texture() {
286 Ok(frame) => return Ok(frame),
287 Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
288 tracing::debug!("Surface lost/outdated, reconfiguring...");
290 self.surface.configure(self.context.device(), &self.config);
291 }
292 Err(wgpu::SurfaceError::OutOfMemory) => {
293 return Err(GraphicsError::SurfaceOutOfMemory);
294 }
295 Err(wgpu::SurfaceError::Timeout) => {
296 return Err(GraphicsError::SurfaceTimeout);
297 }
298 Err(e) => {
299 return Err(GraphicsError::SurfaceTextureAcquisitionFailed(e.to_string()));
300 }
301 }
302
303 match self.surface.get_current_texture() {
305 Ok(frame) => Ok(frame),
306 Err(wgpu::SurfaceError::Lost) => Err(GraphicsError::SurfaceLost),
307 Err(wgpu::SurfaceError::Outdated) => Err(GraphicsError::SurfaceOutdated),
308 Err(wgpu::SurfaceError::OutOfMemory) => Err(GraphicsError::SurfaceOutOfMemory),
309 Err(wgpu::SurfaceError::Timeout) => Err(GraphicsError::SurfaceTimeout),
310 Err(e) => Err(GraphicsError::SurfaceTextureAcquisitionFailed(e.to_string())),
311 }
312 }
313}
314
315impl WindowContext {
316 pub(crate) fn try_begin_drawing_with_profiler(
321 &mut self,
322 gpu_profiler: Option<Arc<GpuFrameProfiler>>,
323 ) -> Result<FrameContext, GraphicsError> {
324 profile_function!();
325
326 let mut configure_needed = false;
327 if let Some(new_size) = self.reconfigure.resize.take() {
328 self.config.width = new_size.width;
329 self.config.height = new_size.height;
330 configure_needed = true;
331 }
332
333 if configure_needed {
334 self.surface.configure(self.context.device(), &self.config);
335 }
336
337 let frame = self.try_acquire_surface_texture()?;
338 let view = frame
339 .texture
340 .create_view(&wgpu::TextureViewDescriptor::default());
341
342 let encoder = self
343 .context
344 .device()
345 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
346 label: Some("Frame Encoder"),
347 });
348
349 Ok(FrameContext {
350 surface: Some(Surface {
351 texture: frame,
352 view,
353 }),
354 encoder: Some(encoder),
355 context: self.context.clone(),
356 stats: FrameStats::new(),
357 window: self.window.window.clone(),
358 surface_format: self.config.format,
359 gpu_profiler,
360 })
361 }
362}
363
364impl WindowBackend for WindowContext {
365 type FrameContext = FrameContext;
366 type Error = GraphicsError;
367
368 fn try_begin_drawing(&mut self) -> Result<Self::FrameContext, Self::Error> {
369 self.try_begin_drawing_with_profiler(None)
370 }
371}
372
373pub struct RenderableWindow {
387 pub(crate) context: WindowContext,
388 pub(crate) gpu_profiler: Option<Arc<GpuFrameProfiler>>,
389}
390
391impl RenderableWindow {
392 pub fn new(window: Window, context: Arc<GraphicsContext>) -> Result<Self, GraphicsError> {
393 Self::new_with_descriptor(window, context, WindowContextDescriptor::default())
394 }
395
396 pub fn new_with_descriptor(
397 window: Window,
398 context: Arc<GraphicsContext>,
399 descriptor: WindowContextDescriptor,
400 ) -> Result<Self, GraphicsError> {
401 profile_function!();
402 let context = WindowContext::new(window, context, descriptor)?;
403 Ok(Self {
404 context,
405 gpu_profiler: None,
406 })
407 }
408
409 pub fn id(&self) -> WindowId {
410 self.context.window.id()
411 }
412
413 pub fn window(&self) -> &Window {
414 &self.context.window
415 }
416
417 pub fn context(&self) -> &WindowContext {
418 &self.context
419 }
420
421 pub fn context_mut(&mut self) -> &mut WindowContext {
422 &mut self.context
423 }
424
425 pub fn surface_format(&self) -> wgpu::TextureFormat {
431 self.context.surface_format()
432 }
433
434 pub fn resized(&mut self, new_size: LogicalSize<u32>) {
436 self.context.resized(new_size);
437 }
438
439 pub fn resized_physical(&mut self, new_size: PhysicalSize<u32>) {
441 self.context.resized_physical(new_size);
442 }
443
444 pub fn physical_size(&self) -> PhysicalSize<u32> {
446 self.context.physical_size()
447 }
448
449 pub fn scale_factor(&self) -> ScaleFactor {
451 self.window().scale_factor()
452 }
453
454 pub fn viewport(&self) -> Viewport {
456 let physical_size = self.physical_size();
457 let scale_factor = self.scale_factor();
458
459 Viewport {
460 position: PhysicalPosition::new(0.0, 0.0),
461 size: PhysicalSize::new(physical_size.width as f32, physical_size.height as f32),
462 scale_factor,
463 }
464 }
465
466 pub fn set_gpu_profiler(&mut self, profiler: Arc<GpuFrameProfiler>) {
488 self.gpu_profiler = Some(profiler);
489 }
490
491 pub fn remove_gpu_profiler(&mut self) -> Option<Arc<GpuFrameProfiler>> {
495 self.gpu_profiler.take()
496 }
497
498 pub fn gpu_profiler(&self) -> Option<&Arc<GpuFrameProfiler>> {
500 self.gpu_profiler.as_ref()
501 }
502}
503
504impl std::ops::Deref for RenderableWindow {
505 type Target = WindowContext;
506
507 fn deref(&self) -> &Self::Target {
508 &self.context
509 }
510}
511
512impl std::ops::DerefMut for RenderableWindow {
513 fn deref_mut(&mut self) -> &mut Self::Target {
514 &mut self.context
515 }
516}
517
518impl WindowBackend for RenderableWindow {
519 type FrameContext = FrameContext;
520 type Error = GraphicsError;
521
522 fn try_begin_drawing(&mut self) -> Result<Self::FrameContext, Self::Error> {
523 self.context
524 .try_begin_drawing_with_profiler(self.gpu_profiler.clone())
525 }
526}