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