srs2dge_core/target/
surface.rs

1use std::{
2    ops::{Deref, DerefMut},
3    sync::Arc,
4};
5use wgpu::{
6    Adapter, Device, Instance, PresentMode, SurfaceConfiguration, SurfaceError, SurfaceTexture,
7    TextureFormat, TextureUsages,
8};
9use winit::window::Window;
10
11//
12
13pub struct ISurface {
14    instance: Arc<Instance>,
15    surface: wgpu::Surface,
16    window: Arc<Window>,
17}
18
19pub struct Surface {
20    device: Arc<Device>,
21    surface: ISurface,
22    format: TextureFormat,
23    present_mode: PresentMode,
24
25    width: u32,
26    height: u32,
27}
28
29//
30
31impl ISurface {
32    pub fn new(window: Arc<Window>, instance: Arc<Instance>) -> Self {
33        // SAFETY: the window is held in an `Arc`.
34        // It is dropped before window is dropped,
35        // because it will be the first elem in this
36        // struct.
37        //
38        // `create_surface` requires "Raw Window Handle
39        // must be a valid object to create a surface
40        // upon and must remain valid for the lifetime
41        // of the returned surface."
42        let surface = unsafe { instance.create_surface(window.as_ref()) };
43
44        Self {
45            instance,
46            surface,
47            window,
48        }
49    }
50
51    pub fn complete(self, adapter: &Adapter, device: Arc<Device>) -> Surface {
52        let surface = self;
53        let format = surface
54            .surface
55            .get_preferred_format(adapter)
56            .expect("Surface is not incompatible");
57
58        let mut surface = Surface {
59            device,
60            surface,
61            format,
62            present_mode: PresentMode::Mailbox,
63
64            width: 0, // properly configured in just a bit
65            height: 0,
66        };
67        surface.configure();
68        surface
69    }
70
71    pub fn get_window(&self) -> Arc<Window> {
72        self.window.clone()
73    }
74}
75
76impl Surface {
77    pub fn set_vsync(&mut self, on: bool) {
78        let new = if on {
79            PresentMode::Mailbox // fallbacks to Fifo if not available
80        } else {
81            PresentMode::Immediate
82        };
83        let updated = self.present_mode != new;
84        self.present_mode = new;
85
86        if updated {
87            self.configure();
88        }
89    }
90
91    pub fn get_vsync(&self) -> bool {
92        match self.present_mode {
93            PresentMode::Immediate => false,
94            PresentMode::Mailbox => true,
95            PresentMode::Fifo => true,
96        }
97    }
98
99    pub fn configure(&mut self) {
100        let window = self.surface.window.as_ref();
101        let size = window.inner_size();
102        let (width, height) = (size.width, size.height);
103        let format = self.format;
104
105        self.width = width;
106        self.height = height;
107        self.surface.surface.configure(
108            &self.device,
109            &SurfaceConfiguration {
110                usage: TextureUsages::RENDER_ATTACHMENT,
111                format,
112                width,
113                height,
114                present_mode: self.present_mode,
115            },
116        );
117    }
118
119    pub fn recreate(&mut self) {
120        let window = self.surface.window.clone();
121        let instance = self.surface.instance.clone();
122        self.surface = ISurface::new(window, instance);
123    }
124
125    pub fn acquire(&mut self) -> SurfaceTexture {
126        loop {
127            match self.surface.get_current_texture() {
128                // got texture
129                Ok(
130                    texture @ SurfaceTexture {
131                        suboptimal: false, ..
132                    },
133                ) => return texture,
134
135                // the only unrecoverable error: out of memory
136                Err(SurfaceError::OutOfMemory) => panic!("Out of memory"),
137
138                // retry
139                Err(SurfaceError::Timeout) => {
140                    log::debug!("Timeout");
141                }
142
143                // recreate the surface
144                Err(SurfaceError::Lost) => {
145                    log::debug!("Lost");
146                    self.recreate();
147                }
148
149                // recreate the swapchain
150                x @ (Ok(SurfaceTexture {
151                    suboptimal: true, ..
152                })
153                | Err(SurfaceError::Outdated)) => {
154                    log::debug!("Outdated | Suboptimal");
155                    drop(x);
156                    self.configure();
157                }
158            }
159        }
160    }
161
162    pub fn format(&self) -> TextureFormat {
163        self.format
164    }
165
166    pub fn get_window(&self) -> Arc<Window> {
167        self.surface.get_window()
168    }
169
170    pub fn get_dim(&self) -> (u32, u32) {
171        (self.width, self.height)
172    }
173}
174
175impl Deref for ISurface {
176    type Target = wgpu::Surface;
177
178    fn deref(&self) -> &Self::Target {
179        &self.surface
180    }
181}
182
183impl DerefMut for ISurface {
184    fn deref_mut(&mut self) -> &mut Self::Target {
185        &mut self.surface
186    }
187}
188
189impl Deref for Surface {
190    type Target = wgpu::Surface;
191
192    fn deref(&self) -> &Self::Target {
193        &self.surface
194    }
195}
196
197impl DerefMut for Surface {
198    fn deref_mut(&mut self) -> &mut Self::Target {
199        &mut self.surface
200    }
201}