1use astrelis_core::{
2 geometry::{LogicalSize, PhysicalPosition, PhysicalSize, ScaleFactor},
3 profiling::profile_function,
4};
5use astrelis_winit::{
6 WindowId,
7 window::{Window, WindowBackend},
8};
9use std::sync::Arc;
10
11use crate::{
12 context::{GraphicsContext, GraphicsError},
13 frame::{FrameContext, FrameStats, Surface},
14};
15
16#[derive(Debug, Clone, Copy)]
21pub struct Viewport {
22 pub position: PhysicalPosition<f32>,
24 pub size: PhysicalSize<f32>,
26 pub scale_factor: ScaleFactor,
28}
29
30impl Default for Viewport {
31 fn default() -> Self {
32 Self {
33 position: PhysicalPosition::new(0.0, 0.0),
34 size: PhysicalSize::new(800.0, 600.0),
35 scale_factor: ScaleFactor(1.0),
37 }
38 }
39}
40
41impl Viewport {
42 pub fn new(width: f32, height: f32, scale_factor: ScaleFactor) -> Self {
44 Self {
45 position: PhysicalPosition::new(0.0, 0.0),
46 size: PhysicalSize::new(width, height),
47 scale_factor,
48 }
49 }
50
51 pub fn from_physical_size(size: PhysicalSize<u32>, scale_factor: ScaleFactor) -> Self {
53 Self {
54 position: PhysicalPosition::new(0.0, 0.0),
55 size: PhysicalSize::new(size.width as f32, size.height as f32),
56 scale_factor,
57 }
58 }
59
60 pub fn is_valid(&self) -> bool {
62 self.size.width > 0.0 && self.size.height > 0.0 && self.scale_factor.0 > 0.0
63 }
64
65 pub fn to_logical(&self) -> LogicalSize<f32> {
67 self.size.to_logical(self.scale_factor)
68 }
69
70 pub fn width(&self) -> f32 {
72 self.size.width
73 }
74
75 pub fn height(&self) -> f32 {
77 self.size.height
78 }
79
80 pub fn x(&self) -> f32 {
82 self.position.x
83 }
84
85 pub fn y(&self) -> f32 {
87 self.position.y
88 }
89}
90
91#[derive(Default)]
93pub struct WindowContextDescriptor {
94 pub format: Option<wgpu::TextureFormat>,
96 pub present_mode: Option<wgpu::PresentMode>,
98 pub alpha_mode: Option<wgpu::CompositeAlphaMode>,
100}
101
102pub struct PendingReconfigure {
103 pub resize: Option<PhysicalSize<u32>>,
104}
105
106impl PendingReconfigure {
107 const fn new() -> Self {
108 Self { resize: None }
109 }
110}
111
112pub struct WindowContext {
114 pub(crate) window: Window,
115 pub(crate) context: Arc<GraphicsContext>,
116 pub(crate) surface: wgpu::Surface<'static>,
117 pub(crate) config: wgpu::SurfaceConfiguration,
118 pub(crate) reconfigure: PendingReconfigure,
119}
120
121impl WindowContext {
122 pub fn new(
123 window: Window,
124 context: Arc<GraphicsContext>,
125 descriptor: WindowContextDescriptor,
126 ) -> Result<Self, GraphicsError> {
127 let scale_factor = window.scale_factor();
128 let logical_size = window.logical_size();
129 let physical_size = logical_size.to_physical(scale_factor);
130
131 let surface = context
132 .instance
133 .create_surface(window.window.clone())
134 .map_err(|e| GraphicsError::SurfaceCreationFailed(e.to_string()))?;
135
136 let mut config = surface
137 .get_default_config(&context.adapter, physical_size.width, physical_size.height)
138 .ok_or_else(|| GraphicsError::SurfaceConfigurationFailed(
139 "No suitable surface configuration found".to_string()
140 ))?;
141
142 if let Some(format) = descriptor.format {
143 config.format = format;
144 }
145 if let Some(present_mode) = descriptor.present_mode {
146 config.present_mode = present_mode;
147 }
148 if let Some(alpha_mode) = descriptor.alpha_mode {
149 config.alpha_mode = alpha_mode;
150 }
151
152 surface.configure(&context.device, &config);
153
154 Ok(Self {
155 window,
156 surface,
157 config,
158 reconfigure: PendingReconfigure::new(),
159 context,
160 })
161 }
162
163 pub fn resized(&mut self, new_size: LogicalSize<u32>) {
165 let scale_factor = self.window.scale_factor();
166 let physical_size = new_size.to_physical(scale_factor);
167 self.reconfigure.resize = Some(physical_size);
168 }
169
170 pub fn resized_physical(&mut self, new_size: PhysicalSize<u32>) {
172 self.reconfigure.resize = Some(new_size);
173 }
174
175 pub fn window(&self) -> &Window {
176 &self.window
177 }
178
179 pub fn graphics_context(&self) -> &GraphicsContext {
180 &self.context
181 }
182
183 pub fn surface(&self) -> &wgpu::Surface<'static> {
184 &self.surface
185 }
186
187 pub fn surface_config(&self) -> &wgpu::SurfaceConfiguration {
188 &self.config
189 }
190
191 pub fn logical_size(&self) -> LogicalSize<u32> {
193 self.window.logical_size()
194 }
195
196 pub fn physical_size(&self) -> PhysicalSize<u32> {
198 self.window.physical_size()
199 }
200
201 pub fn logical_size_f32(&self) -> LogicalSize<f32> {
203 let size = self.logical_size();
204 LogicalSize::new(size.width as f32, size.height as f32)
205 }
206
207 pub fn physical_size_f32(&self) -> PhysicalSize<f32> {
209 let size = self.physical_size();
210 PhysicalSize::new(size.width as f32, size.height as f32)
211 }
212
213 pub fn reconfigure_surface(&mut self, config: wgpu::SurfaceConfiguration) {
215 self.config = config;
216 self.surface.configure(&self.context.device, &self.config);
217 }
218}
219
220impl WindowBackend for WindowContext {
221 type FrameContext = FrameContext;
222
223 fn begin_drawing(&mut self) -> Self::FrameContext {
224 profile_function!();
225
226 let mut configure_needed = false;
227 if let Some(new_size) = self.reconfigure.resize.take() {
228 self.config.width = new_size.width;
229 self.config.height = new_size.height;
230 configure_needed = true;
231 }
232
233 if configure_needed {
234 self.surface.configure(&self.context.device, &self.config);
235 }
236
237 let frame = self.surface.get_current_texture().unwrap_or_else(|e| {
238 tracing::error!("Failed to acquire surface texture: {}. This may indicate the surface is lost or outdated.", e);
239 panic!("Surface texture acquisition failed: {}. Consider handling surface recreation in your application.", e);
240 });
241 let view = frame
242 .texture
243 .create_view(&wgpu::TextureViewDescriptor::default());
244
245 let encoder = self
246 .context
247 .device
248 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
249 label: Some("Frame Encoder"),
250 });
251
252 FrameContext {
253 surface: Some(Surface {
254 texture: frame,
255 view,
256 }),
257 encoder: Some(encoder),
258 context: self.context.clone(),
259 stats: FrameStats::new(),
260 window: self.window.window.clone(),
261 surface_format: self.config.format,
262 }
263 }
264}
265
266pub struct RenderableWindow {
268 pub(crate) context: WindowContext,
269}
270
271impl RenderableWindow {
272 pub fn new(window: Window, context: Arc<GraphicsContext>) -> Result<Self, GraphicsError> {
273 Self::new_with_descriptor(window, context, WindowContextDescriptor::default())
274 }
275
276 pub fn new_with_descriptor(
277 window: Window,
278 context: Arc<GraphicsContext>,
279 descriptor: WindowContextDescriptor,
280 ) -> Result<Self, GraphicsError> {
281 let context = WindowContext::new(window, context, descriptor)?;
282 Ok(Self { context })
283 }
284
285 pub fn id(&self) -> WindowId {
286 self.context.window.id()
287 }
288
289 pub fn window(&self) -> &Window {
290 &self.context.window
291 }
292
293 pub fn context(&self) -> &WindowContext {
294 &self.context
295 }
296
297 pub fn context_mut(&mut self) -> &mut WindowContext {
298 &mut self.context
299 }
300
301 pub fn resized(&mut self, new_size: LogicalSize<u32>) {
303 self.context.resized(new_size);
304 }
305
306 pub fn resized_physical(&mut self, new_size: PhysicalSize<u32>) {
308 self.context.resized_physical(new_size);
309 }
310
311 pub fn physical_size(&self) -> PhysicalSize<u32> {
313 self.context.physical_size()
314 }
315
316 pub fn scale_factor(&self) -> ScaleFactor {
318 self.window().scale_factor()
319 }
320
321 pub fn viewport(&self) -> Viewport {
323 let physical_size = self.physical_size();
324 let scale_factor = self.scale_factor();
325
326 Viewport {
327 position: PhysicalPosition::new(0.0, 0.0),
328 size: PhysicalSize::new(physical_size.width as f32, physical_size.height as f32),
329 scale_factor,
330 }
331 }
332}
333
334impl std::ops::Deref for RenderableWindow {
335 type Target = WindowContext;
336
337 fn deref(&self) -> &Self::Target {
338 &self.context
339 }
340}
341
342impl std::ops::DerefMut for RenderableWindow {
343 fn deref_mut(&mut self) -> &mut Self::Target {
344 &mut self.context
345 }
346}
347
348impl WindowBackend for RenderableWindow {
349 type FrameContext = FrameContext;
350
351 fn begin_drawing(&mut self) -> Self::FrameContext {
352 self.context.begin_drawing()
353 }
354}