blade_graphics/vulkan/
surface.rs

1use ash::vk;
2use std::mem;
3
4impl super::Surface {
5    pub fn info(&self) -> crate::SurfaceInfo {
6        crate::SurfaceInfo {
7            format: self.swapchain.format,
8            alpha: self.swapchain.alpha,
9        }
10    }
11
12    unsafe fn deinit_swapchain(&mut self, raw_device: &ash::Device) {
13        let _ = raw_device.device_wait_idle();
14        self.device
15            .destroy_swapchain(mem::take(&mut self.swapchain.raw), None);
16        for frame in self.frames.drain(..) {
17            raw_device.destroy_image_view(frame.view, None);
18            raw_device.destroy_semaphore(frame.acquire_semaphore, None);
19            raw_device.destroy_semaphore(frame.present_semaphore, None);
20        }
21    }
22
23    pub fn acquire_frame(&mut self) -> super::Frame {
24        let acquire_semaphore = self.next_semaphore;
25        match unsafe {
26            self.device.acquire_next_image(
27                self.swapchain.raw,
28                !0,
29                acquire_semaphore,
30                vk::Fence::null(),
31            )
32        } {
33            Ok((index, _suboptimal)) => {
34                self.next_semaphore = mem::replace(
35                    &mut self.frames[index as usize].acquire_semaphore,
36                    acquire_semaphore,
37                );
38                super::Frame {
39                    internal: self.frames[index as usize],
40                    swapchain: self.swapchain,
41                    image_index: Some(index),
42                }
43            }
44            Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => {
45                log::warn!("Acquire failed because the surface is out of date");
46                super::Frame {
47                    internal: self.frames[0],
48                    swapchain: self.swapchain,
49                    image_index: None,
50                }
51            }
52            Err(other) => panic!("Aquire image error {}", other),
53        }
54    }
55}
56
57impl super::Context {
58    pub fn create_surface<
59        I: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle,
60    >(
61        &self,
62        window: &I,
63    ) -> Result<super::Surface, crate::NotSupportedError> {
64        let khr_swapchain = self
65            .device
66            .swapchain
67            .clone()
68            .ok_or(crate::NotSupportedError::NoSupportedDeviceFound)?;
69
70        let raw = unsafe {
71            ash_window::create_surface(
72                &self.entry,
73                &self.instance.core,
74                window.display_handle().unwrap().as_raw(),
75                window.window_handle().unwrap().as_raw(),
76                None,
77            )
78            .map_err(super::PlatformError::Init)?
79        };
80
81        let khr_surface = self
82            .instance
83            .surface
84            .as_ref()
85            .ok_or(crate::NotSupportedError::PlatformNotSupported)?;
86        if unsafe {
87            khr_surface.get_physical_device_surface_support(
88                self.physical_device,
89                self.queue_family_index,
90                raw,
91            ) != Ok(true)
92        } {
93            log::warn!("Rejected for not presenting to the window surface");
94            return Err(crate::NotSupportedError::PlatformNotSupported);
95        }
96
97        let mut surface_info = vk::PhysicalDeviceSurfaceInfo2KHR {
98            surface: raw,
99            ..Default::default()
100        };
101        let mut fullscreen_exclusive_win32 = vk::SurfaceFullScreenExclusiveWin32InfoEXT::default();
102        surface_info = surface_info.push_next(&mut fullscreen_exclusive_win32);
103        let mut fullscreen_exclusive_ext = vk::SurfaceCapabilitiesFullScreenExclusiveEXT::default();
104        let mut capabilities2_khr =
105            vk::SurfaceCapabilities2KHR::default().push_next(&mut fullscreen_exclusive_ext);
106        let _ = unsafe {
107            self.instance
108                .get_surface_capabilities2
109                .as_ref()
110                .unwrap()
111                .get_physical_device_surface_capabilities2(
112                    self.physical_device,
113                    &surface_info,
114                    &mut capabilities2_khr,
115                )
116        };
117        log::debug!("{:?}", capabilities2_khr.surface_capabilities);
118
119        let semaphore_create_info = vk::SemaphoreCreateInfo::default();
120        let next_semaphore = unsafe {
121            self.device
122                .core
123                .create_semaphore(&semaphore_create_info, None)
124                .unwrap()
125        };
126
127        Ok(super::Surface {
128            device: khr_swapchain,
129            raw,
130            frames: Vec::new(),
131            next_semaphore,
132            swapchain: super::Swapchain {
133                raw: vk::SwapchainKHR::null(),
134                format: crate::TextureFormat::Rgba8Unorm,
135                alpha: crate::AlphaMode::Ignored,
136                target_size: [0; 2],
137            },
138            full_screen_exclusive: fullscreen_exclusive_ext.full_screen_exclusive_supported != 0,
139        })
140    }
141
142    pub fn destroy_surface(&self, surface: &mut super::Surface) {
143        unsafe {
144            surface.deinit_swapchain(&self.device.core);
145            self.device
146                .core
147                .destroy_semaphore(surface.next_semaphore, None)
148        };
149        if let Some(ref surface_instance) = self.instance.surface {
150            unsafe { surface_instance.destroy_surface(surface.raw, None) };
151        }
152    }
153
154    pub fn reconfigure_surface(&self, surface: &mut super::Surface, config: crate::SurfaceConfig) {
155        let khr_surface = self.instance.surface.as_ref().unwrap();
156
157        let capabilities = unsafe {
158            khr_surface
159                .get_physical_device_surface_capabilities(self.physical_device, surface.raw)
160                .unwrap()
161        };
162        if config.size.width < capabilities.min_image_extent.width
163            || config.size.width > capabilities.max_image_extent.width
164            || config.size.height < capabilities.min_image_extent.height
165            || config.size.height > capabilities.max_image_extent.height
166        {
167            log::warn!(
168                "Requested size {}x{} is outside of surface capabilities",
169                config.size.width,
170                config.size.height
171            );
172        }
173
174        let (alpha, composite_alpha) = if config.transparent {
175            if capabilities
176                .supported_composite_alpha
177                .contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED)
178            {
179                (
180                    crate::AlphaMode::PostMultiplied,
181                    vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
182                )
183            } else if capabilities
184                .supported_composite_alpha
185                .contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED)
186            {
187                (
188                    crate::AlphaMode::PreMultiplied,
189                    vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
190                )
191            } else {
192                log::error!(
193                    "No composite alpha flag for transparency: {:?}",
194                    capabilities.supported_composite_alpha
195                );
196                (
197                    crate::AlphaMode::Ignored,
198                    vk::CompositeAlphaFlagsKHR::OPAQUE,
199                )
200            }
201        } else {
202            (
203                crate::AlphaMode::Ignored,
204                vk::CompositeAlphaFlagsKHR::OPAQUE,
205            )
206        };
207
208        let (requested_frame_count, mode_preferences) = match config.display_sync {
209            crate::DisplaySync::Block => (3, [vk::PresentModeKHR::FIFO].as_slice()),
210            crate::DisplaySync::Recent => (
211                3,
212                [
213                    vk::PresentModeKHR::MAILBOX,
214                    vk::PresentModeKHR::FIFO_RELAXED,
215                    vk::PresentModeKHR::IMMEDIATE,
216                ]
217                .as_slice(),
218            ),
219            crate::DisplaySync::Tear => (2, [vk::PresentModeKHR::IMMEDIATE].as_slice()),
220        };
221        let effective_frame_count = requested_frame_count.max(capabilities.min_image_count);
222
223        let present_modes = unsafe {
224            khr_surface
225                .get_physical_device_surface_present_modes(self.physical_device, surface.raw)
226                .unwrap()
227        };
228        let present_mode = *mode_preferences
229            .iter()
230            .find(|mode| present_modes.contains(mode))
231            .unwrap();
232        log::info!("Using surface present mode {:?}", present_mode);
233
234        let queue_families = [self.queue_family_index];
235
236        let mut supported_formats = Vec::new();
237        let (format, surface_format) = if surface.swapchain.target_size[0] > 0 {
238            let format = surface.swapchain.format;
239            log::info!("Retaining current format: {:?}", format);
240            let vk_color_space = match (format, config.color_space) {
241                (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Srgb) => {
242                    vk::ColorSpaceKHR::SRGB_NONLINEAR
243                }
244                (crate::TextureFormat::Bgra8Unorm, crate::ColorSpace::Linear) => {
245                    vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT
246                }
247                (crate::TextureFormat::Bgra8UnormSrgb, crate::ColorSpace::Linear) => {
248                    vk::ColorSpaceKHR::default()
249                }
250                _ => panic!(
251                    "Unexpected format {:?} under color space {:?}",
252                    format, config.color_space
253                ),
254            };
255            (
256                format,
257                vk::SurfaceFormatKHR {
258                    format: super::map_texture_format(format),
259                    color_space: vk_color_space,
260                },
261            )
262        } else {
263            supported_formats = unsafe {
264                khr_surface
265                    .get_physical_device_surface_formats(self.physical_device, surface.raw)
266                    .unwrap()
267            };
268            match config.color_space {
269                crate::ColorSpace::Linear => {
270                    let surface_format = vk::SurfaceFormatKHR {
271                        format: vk::Format::B8G8R8A8_UNORM,
272                        color_space: vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
273                    };
274                    if supported_formats.contains(&surface_format) {
275                        log::info!("Using linear SRGB color space");
276                        (crate::TextureFormat::Bgra8Unorm, surface_format)
277                    } else {
278                        (
279                            crate::TextureFormat::Bgra8UnormSrgb,
280                            vk::SurfaceFormatKHR {
281                                format: vk::Format::B8G8R8A8_SRGB,
282                                color_space: vk::ColorSpaceKHR::default(),
283                            },
284                        )
285                    }
286                }
287                crate::ColorSpace::Srgb => (
288                    crate::TextureFormat::Bgra8Unorm,
289                    vk::SurfaceFormatKHR {
290                        format: vk::Format::B8G8R8A8_UNORM,
291                        color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR,
292                    },
293                ),
294            }
295        };
296        if !supported_formats.is_empty() && !supported_formats.contains(&surface_format) {
297            log::error!("Surface formats are incompatible: {:?}", supported_formats);
298        }
299
300        let vk_usage = super::resource::map_texture_usage(config.usage, crate::TexelAspects::COLOR);
301        if !capabilities.supported_usage_flags.contains(vk_usage) {
302            log::error!(
303                "Surface usages are incompatible: {:?}",
304                capabilities.supported_usage_flags
305            );
306        }
307
308        let mut full_screen_exclusive_info = vk::SurfaceFullScreenExclusiveInfoEXT {
309            full_screen_exclusive: if config.allow_exclusive_full_screen {
310                vk::FullScreenExclusiveEXT::ALLOWED
311            } else {
312                vk::FullScreenExclusiveEXT::DISALLOWED
313            },
314            ..Default::default()
315        };
316
317        let mut create_info = vk::SwapchainCreateInfoKHR {
318            surface: surface.raw,
319            min_image_count: effective_frame_count,
320            image_format: surface_format.format,
321            image_color_space: surface_format.color_space,
322            image_extent: vk::Extent2D {
323                width: config.size.width,
324                height: config.size.height,
325            },
326            image_array_layers: 1,
327            image_usage: vk_usage,
328            pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
329            composite_alpha,
330            present_mode,
331            old_swapchain: surface.swapchain.raw,
332            ..Default::default()
333        }
334        .queue_family_indices(&queue_families);
335
336        if surface.full_screen_exclusive {
337            assert!(self.device.full_screen_exclusive.is_some());
338            create_info = create_info.push_next(&mut full_screen_exclusive_info);
339            log::info!(
340                "Configuring exclusive full screen: {}",
341                config.allow_exclusive_full_screen
342            );
343        }
344        let raw_swapchain = unsafe { surface.device.create_swapchain(&create_info, None).unwrap() };
345
346        unsafe {
347            surface.deinit_swapchain(&self.device.core);
348        }
349
350        let images = unsafe { surface.device.get_swapchain_images(raw_swapchain).unwrap() };
351        let target_size = [config.size.width as u16, config.size.height as u16];
352        let subresource_range = vk::ImageSubresourceRange {
353            aspect_mask: vk::ImageAspectFlags::COLOR,
354            base_mip_level: 0,
355            level_count: 1,
356            base_array_layer: 0,
357            layer_count: 1,
358        };
359        for image in images {
360            let view_create_info = vk::ImageViewCreateInfo {
361                image,
362                view_type: vk::ImageViewType::TYPE_2D,
363                format: surface_format.format,
364                subresource_range,
365                ..Default::default()
366            };
367            let view = unsafe {
368                self.device
369                    .core
370                    .create_image_view(&view_create_info, None)
371                    .unwrap()
372            };
373            let semaphore_create_info = vk::SemaphoreCreateInfo::default();
374            let acquire_semaphore = unsafe {
375                self.device
376                    .core
377                    .create_semaphore(&semaphore_create_info, None)
378                    .unwrap()
379            };
380            let present_semaphore = unsafe {
381                self.device
382                    .core
383                    .create_semaphore(&semaphore_create_info, None)
384                    .unwrap()
385            };
386            surface.frames.push(super::InternalFrame {
387                acquire_semaphore,
388                present_semaphore,
389                image,
390                view,
391            });
392        }
393        surface.swapchain = super::Swapchain {
394            raw: raw_swapchain,
395            format,
396            alpha,
397            target_size,
398        };
399    }
400}