Skip to main content

vk_graph/driver/
surface.rs

1//! Native platform window surface types.
2
3use {
4    super::{DriverError, device::Device, instance::Instance},
5    ash::vk,
6    ash_window::create_surface,
7    log::warn,
8    raw_window_handle::{HasDisplayHandle, HasWindowHandle},
9    std::{
10        fmt::{Debug, Formatter},
11        thread::panicking,
12    },
13};
14
15/// Smart pointer handle to a [`vk::SurfaceKHR`] object.
16#[read_only::cast]
17pub struct Surface {
18    /// The device which owns this surface resource.
19    ///
20    /// _Note:_ This field is read-only.
21    pub device: Device,
22
23    /// The native Vulkan resource handle of this surface.
24    ///
25    /// _Note:_ This field is read-only.
26    pub handle: vk::SurfaceKHR,
27}
28
29impl Surface {
30    /// Query surface capabilities
31    pub fn capabilities(&self) -> Result<vk::SurfaceCapabilitiesKHR, DriverError> {
32        let surface_ext = Device::expect_surface_ext(&self.device);
33
34        unsafe {
35            surface_ext.get_physical_device_surface_capabilities(
36                self.device.physical_device.handle,
37                self.handle,
38            )
39        }
40        .map_err(|err| {
41            warn!("unable to get surface capabilities: {err}");
42
43            DriverError::Unsupported
44        })
45    }
46
47    /// Create a surface from a raw window display handle.
48    ///
49    /// `device` must have been created with platform specific surface extensions enabled.
50    #[profiling::function]
51    pub fn create(
52        device: &Device,
53        display: impl HasDisplayHandle,
54        window: impl HasWindowHandle,
55    ) -> Result<Self, DriverError> {
56        let device = device.clone();
57
58        let display_handle = display.display_handle().map_err(|err| {
59            warn!("unable to get display handle: {err}");
60
61            DriverError::Unsupported
62        })?;
63        let window_handle = window.window_handle().map_err(|err| {
64            warn!("unable to get window handle: {err}");
65
66            DriverError::Unsupported
67        })?;
68
69        let handle = unsafe {
70            create_surface(
71                Instance::entry(&device.physical_device.instance),
72                &device.physical_device.instance,
73                display_handle.as_raw(),
74                window_handle.as_raw(),
75                None,
76            )
77        }
78        .map_err(|err| {
79            warn!("unable to create surface: {err}");
80
81            DriverError::Unsupported
82        })?;
83
84        Ok(Self { device, handle })
85    }
86
87    /// Lists the supported surface formats.
88    #[profiling::function]
89    pub fn formats(&self) -> Result<Vec<vk::SurfaceFormatKHR>, DriverError> {
90        let surface_ext = Device::expect_surface_ext(&self.device);
91
92        unsafe {
93            surface_ext.get_physical_device_surface_formats(
94                self.device.physical_device.handle,
95                self.handle,
96            )
97        }
98        .map_err(|err| {
99            warn!("unable to get surface formats: {err}");
100
101            DriverError::Unsupported
102        })
103    }
104
105    /// Helper function to automatically select the best UNORM format, if one is available.
106    #[profiling::function]
107    pub fn linear(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
108        formats
109            .iter()
110            .find(|&&vk::SurfaceFormatKHR { format, .. }| {
111                matches!(
112                    format,
113                    vk::Format::R8G8B8A8_UNORM | vk::Format::B8G8R8A8_UNORM
114                )
115            })
116            .copied()
117    }
118
119    /// Helper function to automatically select the best UNORM format.
120    ///
121    /// **_NOTE:_** The default surface format is undefined, and although legal the results _may_
122    /// not support presentation. You should prefer to use [`Surface::linear`] and fall back to
123    /// supported values manually.
124    pub fn linear_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
125        Self::linear(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
126    }
127
128    /// Returns `true` if the given queue family supports presentation on this surface.
129    pub fn physical_device_support(&self, queue_family_index: u32) -> Result<bool, DriverError> {
130        let surface_ext = Device::expect_surface_ext(&self.device);
131
132        unsafe {
133            surface_ext.get_physical_device_surface_support(
134                self.device.physical_device.handle,
135                queue_family_index,
136                self.handle,
137            )
138        }
139        .map_err(|err| {
140            warn!("unable to get physical device support: {err}");
141
142            match err {
143                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
144                    DriverError::OutOfMemory
145                }
146                vk::Result::ERROR_SURFACE_LOST_KHR => DriverError::InvalidData,
147                _ => DriverError::Unsupported,
148            }
149        })
150    }
151
152    /// Query supported presentation modes.
153    pub fn present_modes(&self) -> Result<Vec<vk::PresentModeKHR>, DriverError> {
154        let surface_ext = Device::expect_surface_ext(&self.device);
155
156        unsafe {
157            surface_ext.get_physical_device_surface_present_modes(
158                self.device.physical_device.handle,
159                self.handle,
160            )
161        }
162        .map_err(|err| {
163            warn!("unable to get present modes: {err}");
164
165            DriverError::Unsupported
166        })
167    }
168
169    /// Helper function to automatically select the best sRGB format, if one is available.
170    #[profiling::function]
171    pub fn srgb(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
172        formats
173            .iter()
174            .find(
175                |&&vk::SurfaceFormatKHR {
176                     color_space,
177                     format,
178                 }| {
179                    matches!(color_space, vk::ColorSpaceKHR::SRGB_NONLINEAR)
180                        && matches!(
181                            format,
182                            vk::Format::R8G8B8A8_SRGB | vk::Format::B8G8R8A8_SRGB
183                        )
184                },
185            )
186            .copied()
187    }
188
189    /// Helper function to automatically select the best sRGB format.
190    ///
191    /// **_NOTE:_** The default surface format is undefined, and although legal the results _may_
192    /// not support presentation. You should prefer to use [`Surface::srgb`] and fall back to
193    /// supported values manually.
194    pub fn srgb_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
195        Self::srgb(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
196    }
197}
198
199impl Debug for Surface {
200    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
201        f.write_str("Surface")
202    }
203}
204
205impl Drop for Surface {
206    #[profiling::function]
207    fn drop(&mut self) {
208        if panicking() {
209            return;
210        }
211
212        let surface_ext = Device::expect_surface_ext(&self.device);
213
214        unsafe {
215            surface_ext.destroy_surface(self.handle, None);
216        }
217    }
218}
219
220impl Eq for Surface {}
221
222impl PartialEq for Surface {
223    fn eq(&self, other: &Self) -> bool {
224        self.handle == other.handle
225    }
226}
227
228#[cfg(test)]
229mod test {
230    use super::Surface;
231    use ash::vk;
232
233    #[test]
234    fn linear_prefers_known_unorm_formats() {
235        let formats = [
236            vk::SurfaceFormatKHR {
237                format: vk::Format::R8G8B8A8_SRGB,
238                ..Default::default()
239            },
240            vk::SurfaceFormatKHR {
241                format: vk::Format::R8G8B8A8_UNORM,
242                ..Default::default()
243            },
244        ];
245
246        assert_eq!(
247            Surface::linear(&formats).unwrap().format,
248            vk::Format::R8G8B8A8_UNORM
249        );
250    }
251
252    #[test]
253    fn linear_or_default_falls_back_to_first_format() {
254        let formats = [vk::SurfaceFormatKHR {
255            format: vk::Format::R16G16B16A16_SFLOAT,
256            ..Default::default()
257        }];
258
259        assert_eq!(
260            Surface::linear_or_default(&formats).format,
261            vk::Format::R16G16B16A16_SFLOAT
262        );
263    }
264
265    #[test]
266    fn srgb_prefers_known_srgb_formats() {
267        let formats = [
268            vk::SurfaceFormatKHR {
269                color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
270                format: vk::Format::B8G8R8A8_SRGB,
271            },
272            vk::SurfaceFormatKHR {
273                color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
274                format: vk::Format::R8G8B8A8_SRGB,
275            },
276        ];
277
278        assert_eq!(
279            Surface::srgb(&formats).unwrap().format,
280            vk::Format::R8G8B8A8_SRGB
281        );
282    }
283
284    #[test]
285    fn srgb_or_default_falls_back_to_first_format() {
286        let formats = [vk::SurfaceFormatKHR {
287            color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
288            format: vk::Format::R16G16B16A16_SFLOAT,
289        }];
290
291        assert_eq!(
292            Surface::srgb_or_default(&formats).format,
293            vk::Format::R16G16B16A16_SFLOAT
294        );
295    }
296}