vk_graph/driver/
surface.rs1use {
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#[read_only::cast]
19pub struct Surface {
20 pub device: Device,
24
25 pub handle: vk::SurfaceKHR,
29}
30
31impl Surface {
32 pub fn capabilities(&self) -> Result<vk::SurfaceCapabilitiesKHR, DriverError> {
36 let khr_surface = Device::expect_vk_khr_surface(&self.device);
37
38 unsafe {
39 khr_surface
40 .get_physical_device_surface_capabilities(self.device.physical.handle, self.handle)
41 }
42 .map_err(|err| {
43 warn!("unable to get surface capabilities: {err}");
44
45 DriverError::Unsupported
46 })
47 }
48
49 #[profiling::function]
55 pub fn create(
56 device: &Device,
57 display: impl HasDisplayHandle,
58 window: impl HasWindowHandle,
59 ) -> Result<Self, DriverError> {
60 let device = device.clone();
61
62 let display_handle = display.display_handle().map_err(|err| {
63 warn!("unable to get display handle: {err}");
64
65 DriverError::Unsupported
66 })?;
67 let window_handle = window.window_handle().map_err(|err| {
68 warn!("unable to get window handle: {err}");
69
70 DriverError::Unsupported
71 })?;
72
73 let handle = unsafe {
74 create_surface(
75 Instance::entry(&device.physical.instance),
76 &device.physical.instance,
77 display_handle.as_raw(),
78 window_handle.as_raw(),
79 None,
80 )
81 }
82 .map_err(|err| {
83 warn!("unable to create surface: {err}");
84
85 DriverError::Unsupported
86 })?;
87
88 Ok(Self { device, handle })
89 }
90
91 #[profiling::function]
95 pub fn formats(&self) -> Result<Vec<vk::SurfaceFormatKHR>, DriverError> {
96 let khr_surface = Device::expect_vk_khr_surface(&self.device);
97
98 unsafe {
99 khr_surface
100 .get_physical_device_surface_formats(self.device.physical.handle, self.handle)
101 }
102 .map_err(|err| {
103 warn!("unable to get surface formats: {err}");
104
105 DriverError::Unsupported
106 })
107 }
108
109 #[profiling::function]
111 pub fn linear(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
112 formats
113 .iter()
114 .find(|&&vk::SurfaceFormatKHR { format, .. }| {
115 matches!(
116 format,
117 vk::Format::R8G8B8A8_UNORM | vk::Format::B8G8R8A8_UNORM
118 )
119 })
120 .copied()
121 }
122
123 pub fn linear_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
129 Self::linear(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
130 }
131
132 pub fn composite_alpha_or_default(
134 supported: vk::CompositeAlphaFlagsKHR,
135 requested: vk::CompositeAlphaFlagsKHR,
136 ) -> vk::CompositeAlphaFlagsKHR {
137 if supported.contains(requested) {
138 return requested;
139 }
140
141 if supported.contains(vk::CompositeAlphaFlagsKHR::OPAQUE) {
142 return vk::CompositeAlphaFlagsKHR::OPAQUE;
143 }
144
145 [
146 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
147 vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
148 vk::CompositeAlphaFlagsKHR::INHERIT,
149 ]
150 .into_iter()
151 .find(|mode| supported.contains(*mode))
152 .unwrap_or(requested)
153 }
154
155 pub fn physical_device_support(&self, queue_family_index: u32) -> Result<bool, DriverError> {
159 let khr_surface = Device::expect_vk_khr_surface(&self.device);
160
161 unsafe {
162 khr_surface
163 .get_physical_device_surface_support(
164 self.device.physical.handle,
165 queue_family_index,
166 self.handle,
167 )
168 .map_err(|err| {
169 warn!("unable to get physical device support: {err}");
170
171 match err {
172 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
173 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
174 vk::Result::ERROR_SURFACE_LOST_KHR => DriverError::InvalidData,
175 _ => DriverError::Unsupported,
176 }
177 })
178 }
179 }
180
181 pub fn present_modes(&self) -> Result<Vec<vk::PresentModeKHR>, DriverError> {
185 let khr_surface = Device::expect_vk_khr_surface(&self.device);
186
187 unsafe {
188 khr_surface
189 .get_physical_device_surface_present_modes(self.device.physical.handle, self.handle)
190 .map_err(|err| {
191 warn!("unable to get present modes: {err}");
192
193 DriverError::Unsupported
194 })
195 }
196 }
197
198 #[profiling::function]
200 pub fn srgb(formats: &[vk::SurfaceFormatKHR]) -> Option<vk::SurfaceFormatKHR> {
201 formats
202 .iter()
203 .find(
204 |&&vk::SurfaceFormatKHR {
205 color_space,
206 format,
207 }| {
208 matches!(color_space, vk::ColorSpaceKHR::SRGB_NONLINEAR)
209 && matches!(
210 format,
211 vk::Format::R8G8B8A8_SRGB | vk::Format::B8G8R8A8_SRGB
212 )
213 },
214 )
215 .copied()
216 }
217
218 pub fn srgb_or_default(formats: &[vk::SurfaceFormatKHR]) -> vk::SurfaceFormatKHR {
224 Self::srgb(formats).unwrap_or_else(|| formats.first().copied().unwrap_or_default())
225 }
226}
227
228impl Debug for Surface {
229 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
230 f.debug_struct(stringify!(Surface))
231 .field("handle", &self.handle)
232 .field("device", &self.device)
233 .finish_non_exhaustive()
234 }
235}
236
237impl Drop for Surface {
238 #[profiling::function]
239 fn drop(&mut self) {
240 if panicking() {
241 return;
242 }
243
244 let khr_surface = Device::expect_vk_khr_surface(&self.device);
245
246 unsafe {
247 khr_surface.destroy_surface(self.handle, None);
248 }
249 }
250}
251
252impl Eq for Surface {}
253
254impl PartialEq for Surface {
255 fn eq(&self, other: &Self) -> bool {
256 self.handle == other.handle
257 }
258}
259
260#[cfg(test)]
261mod test {
262 use super::Surface;
263 use ash::vk;
264
265 #[test]
266 fn linear_prefers_known_unorm_formats() {
267 let formats = [
268 vk::SurfaceFormatKHR {
269 format: vk::Format::R8G8B8A8_SRGB,
270 ..Default::default()
271 },
272 vk::SurfaceFormatKHR {
273 format: vk::Format::R8G8B8A8_UNORM,
274 ..Default::default()
275 },
276 ];
277
278 assert_eq!(
279 Surface::linear(&formats).unwrap().format,
280 vk::Format::R8G8B8A8_UNORM
281 );
282 }
283
284 #[test]
285 fn linear_or_default_falls_back_to_first_format() {
286 let formats = [vk::SurfaceFormatKHR {
287 format: vk::Format::R16G16B16A16_SFLOAT,
288 ..Default::default()
289 }];
290
291 assert_eq!(
292 Surface::linear_or_default(&formats).format,
293 vk::Format::R16G16B16A16_SFLOAT
294 );
295 }
296
297 #[test]
298 fn srgb_prefers_known_srgb_formats() {
299 let formats = [
300 vk::SurfaceFormatKHR {
301 color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
302 format: vk::Format::B8G8R8A8_SRGB,
303 },
304 vk::SurfaceFormatKHR {
305 color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
306 format: vk::Format::R8G8B8A8_SRGB,
307 },
308 ];
309
310 assert_eq!(
311 Surface::srgb(&formats).unwrap().format,
312 vk::Format::R8G8B8A8_SRGB
313 );
314 }
315
316 #[test]
317 fn srgb_or_default_falls_back_to_first_format() {
318 let formats = [vk::SurfaceFormatKHR {
319 color_space: vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
320 format: vk::Format::R16G16B16A16_SFLOAT,
321 }];
322
323 assert_eq!(
324 Surface::srgb_or_default(&formats).format,
325 vk::Format::R16G16B16A16_SFLOAT
326 );
327 }
328
329 #[test]
330 fn composite_alpha_prefers_requested_supported_mode() {
331 let supported =
332 vk::CompositeAlphaFlagsKHR::OPAQUE | vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED;
333
334 assert_eq!(
335 Surface::composite_alpha_or_default(
336 supported,
337 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
338 ),
339 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED
340 );
341 }
342
343 #[test]
344 fn composite_alpha_falls_back_to_opaque() {
345 let supported =
346 vk::CompositeAlphaFlagsKHR::OPAQUE | vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED;
347
348 assert_eq!(
349 Surface::composite_alpha_or_default(
350 supported,
351 vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
352 ),
353 vk::CompositeAlphaFlagsKHR::OPAQUE
354 );
355 }
356}