1use super::{VulkanCommandRecorder, VulkanDevice, VulkanTexture};
4use crate::HalError;
5use ash::khr::surface;
6use ash::vk;
7
8pub struct VulkanSwapchain {
9 pub swapchain: vk::SwapchainKHR,
10 pub images: Vec<vk::Image>,
11 pub views: Vec<vk::ImageView>,
12 pub width: u32,
13 pub height: u32,
14 pub format: vk::Format,
15}
16
17impl VulkanDevice {
18 pub fn create_swapchain(
19 &self,
20 surface: vk::SurfaceKHR,
21 surface_loader: &surface::Instance,
22 width: u32,
23 height: u32,
24 old_swapchain: vk::SwapchainKHR,
25 preferred_format: Option<vk::Format>,
26 preferred_present_mode: Option<vk::PresentModeKHR>,
27 ) -> Result<VulkanSwapchain, HalError> {
28 let loader = self.swapchain_loader.as_ref().ok_or_else(|| {
29 HalError::BackendError("VK_KHR_swapchain is not supported or not enabled".to_string())
30 })?;
31
32 let caps = unsafe {
33 surface_loader
34 .get_physical_device_surface_capabilities(self.physical_device, surface)
35 .map_err(|e| {
36 HalError::BackendError(format!("Failed to query surface capabilities: {e}"))
37 })?
38 };
39
40 let formats = unsafe {
41 surface_loader
42 .get_physical_device_surface_formats(self.physical_device, surface)
43 .map_err(|e| {
44 HalError::BackendError(format!("Failed to query surface formats: {e}"))
45 })?
46 };
47
48 let present_modes = unsafe {
49 surface_loader
50 .get_physical_device_surface_present_modes(self.physical_device, surface)
51 .map_err(|e| {
52 HalError::BackendError(format!("Failed to query surface present modes: {e}"))
53 })?
54 };
55
56 if formats.is_empty() {
57 return Err(HalError::BackendError(
58 "No surface formats supported".to_string(),
59 ));
60 }
61
62 let extent = if caps.current_extent.width != u32::MAX {
63 caps.current_extent
64 } else {
65 vk::Extent2D {
66 width: width.clamp(caps.min_image_extent.width, caps.max_image_extent.width),
67 height: height.clamp(caps.min_image_extent.height, caps.max_image_extent.height),
68 }
69 };
70
71 let mut image_count = caps.min_image_count + 1;
72 if caps.max_image_count > 0 && image_count > caps.max_image_count {
73 image_count = caps.max_image_count;
74 }
75
76 let selected_format = if let Some(pref) = preferred_format {
77 formats
78 .iter()
79 .find(|f| f.format == pref)
80 .cloned()
81 .unwrap_or(formats[0])
82 } else {
83 formats
84 .iter()
85 .find(|f| {
86 f.format == vk::Format::B8G8R8A8_UNORM
87 || f.format == vk::Format::R8G8B8A8_UNORM
88 || f.format == vk::Format::B8G8R8A8_SRGB
89 || f.format == vk::Format::R8G8B8A8_SRGB
90 })
91 .cloned()
92 .unwrap_or(formats[0])
93 };
94
95 let present_mode = match preferred_present_mode {
96 Some(mode) if present_modes.contains(&mode) => mode,
97 _ => {
98 if present_modes.contains(&vk::PresentModeKHR::MAILBOX) {
99 vk::PresentModeKHR::MAILBOX
100 } else {
101 vk::PresentModeKHR::FIFO
102 }
103 }
104 };
105
106 let create_info = vk::SwapchainCreateInfoKHR::default()
107 .surface(surface)
108 .min_image_count(image_count)
109 .image_format(selected_format.format)
110 .image_color_space(selected_format.color_space)
111 .image_extent(extent)
112 .image_array_layers(1)
113 .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST)
114 .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
115 .pre_transform(caps.current_transform)
116 .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
117 .present_mode(present_mode)
118 .clipped(true)
119 .old_swapchain(old_swapchain);
120
121 let swapchain = unsafe {
122 loader
123 .create_swapchain(&create_info, None)
124 .map_err(|e| HalError::BackendError(format!("Failed to create swapchain: {e}")))?
125 };
126
127 let images = match unsafe { loader.get_swapchain_images(swapchain) } {
128 Ok(imgs) => imgs,
129 Err(e) => {
130 unsafe {
131 loader.destroy_swapchain(swapchain, None);
132 }
133 return Err(HalError::BackendError(format!(
134 "Failed to get swapchain images: {e}"
135 )));
136 }
137 };
138
139 let mut views = Vec::with_capacity(images.len());
140 for &img in &images {
141 let view_create_info = vk::ImageViewCreateInfo::default()
142 .image(img)
143 .view_type(vk::ImageViewType::TYPE_2D)
144 .format(selected_format.format)
145 .subresource_range(vk::ImageSubresourceRange {
146 aspect_mask: vk::ImageAspectFlags::COLOR,
147 base_mip_level: 0,
148 level_count: 1,
149 base_array_layer: 0,
150 layer_count: 1,
151 });
152
153 let view = match unsafe { self.device.create_image_view(&view_create_info, None) } {
154 Ok(v) => v,
155 Err(e) => {
156 unsafe {
157 for v in views {
158 self.device.destroy_image_view(v, None);
159 }
160 loader.destroy_swapchain(swapchain, None);
161 }
162 return Err(HalError::BackendError(format!(
163 "Failed to create swapchain image view: {e}"
164 )));
165 }
166 };
167 views.push(view);
168 }
169
170 Ok(VulkanSwapchain {
171 swapchain,
172 images,
173 views,
174 width: extent.width,
175 height: extent.height,
176 format: selected_format.format,
177 })
178 }
179
180 pub fn destroy_swapchain(&self, swapchain: VulkanSwapchain) {
181 if let Some(loader) = &self.swapchain_loader {
182 unsafe {
183 for view in swapchain.views {
184 self.device.destroy_image_view(view, None);
185 }
186 loader.destroy_swapchain(swapchain.swapchain, None);
187 }
188 }
189 }
190
191 pub fn acquire_next_image(
192 &self,
193 swapchain: &VulkanSwapchain,
194 semaphore: vk::Semaphore,
195 timeout_nanos: u64,
196 ) -> Result<(u32, bool), HalError> {
197 let loader = self
198 .swapchain_loader
199 .as_ref()
200 .ok_or_else(|| HalError::BackendError("VK_KHR_swapchain is not enabled".to_string()))?;
201 unsafe {
202 loader
203 .acquire_next_image(
204 swapchain.swapchain,
205 timeout_nanos,
206 semaphore,
207 vk::Fence::null(),
208 )
209 .map_err(|e| HalError::BackendError(format!("acquire_next_image failed: {e}")))
210 }
211 }
212
213 pub fn queue_present(
214 &self,
215 swapchain: &VulkanSwapchain,
216 image_index: u32,
217 wait_semaphores: &[vk::Semaphore],
218 ) -> Result<bool, HalError> {
219 self.queue_present_raw(swapchain.swapchain, image_index, wait_semaphores)
220 }
221
222 pub fn queue_present_raw(
223 &self,
224 swapchain: vk::SwapchainKHR,
225 image_index: u32,
226 wait_semaphores: &[vk::Semaphore],
227 ) -> Result<bool, HalError> {
228 let loader = self
229 .swapchain_loader
230 .as_ref()
231 .ok_or_else(|| HalError::BackendError("VK_KHR_swapchain is not enabled".to_string()))?;
232
233 let swapchains = [swapchain];
234 let image_indices = [image_index];
235 let present_info = vk::PresentInfoKHR::default()
236 .wait_semaphores(wait_semaphores)
237 .swapchains(&swapchains)
238 .image_indices(&image_indices);
239
240 let queue = self.queue.lock().unwrap();
241 unsafe {
242 loader
243 .queue_present(*queue, &present_info)
244 .map_err(|e| HalError::BackendError(format!("queue_present failed: {e}")))
245 }
246 }
247
248 pub fn cmd_transition_to_present(
249 &self,
250 recorder: &VulkanCommandRecorder,
251 texture: &VulkanTexture,
252 ) {
253 let old_layout = texture.transition_layout(vk::ImageLayout::PRESENT_SRC_KHR);
254 let new_layout = vk::ImageLayout::PRESENT_SRC_KHR;
255
256 let subresource_range = vk::ImageSubresourceRange::default()
257 .aspect_mask(vk::ImageAspectFlags::COLOR)
258 .base_mip_level(0)
259 .level_count(1)
260 .base_array_layer(0)
261 .layer_count(1);
262
263 let (src_stage, src_access) = match old_layout {
264 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
265 vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
266 vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
267 ),
268 vk::ImageLayout::TRANSFER_SRC_OPTIMAL => (
269 vk::PipelineStageFlags2::TRANSFER,
270 vk::AccessFlags2::TRANSFER_READ,
271 ),
272 _ => (
273 vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT
274 | vk::PipelineStageFlags2::TRANSFER,
275 vk::AccessFlags2::COLOR_ATTACHMENT_WRITE | vk::AccessFlags2::TRANSFER_READ,
276 ),
277 };
278
279 let barrier = vk::ImageMemoryBarrier2::default()
280 .src_stage_mask(src_stage)
281 .src_access_mask(src_access)
282 .dst_stage_mask(vk::PipelineStageFlags2::BOTTOM_OF_PIPE)
283 .dst_access_mask(vk::AccessFlags2::empty())
284 .old_layout(old_layout)
285 .new_layout(new_layout)
286 .image(texture.image)
287 .subresource_range(subresource_range);
288
289 let barriers = [barrier];
290 let dep_info = vk::DependencyInfo::default().image_memory_barriers(&barriers);
291
292 unsafe {
293 self.device
294 .cmd_pipeline_barrier2(recorder.command_buffer, &dep_info);
295 }
296 }
297
298 pub fn submit_for_surface(
299 &self,
300 recorder: VulkanCommandRecorder,
301 wait_semaphore: vk::Semaphore,
302 signal_semaphore: vk::Semaphore,
303 fence: Option<vk::Fence>,
304 ) -> Result<(), HalError> {
305 unsafe {
306 self.device
307 .end_command_buffer(recorder.command_buffer)
308 .map_err(|e| HalError::CommandError(e.to_string()))?;
309
310 let cmd_buffers = [recorder.command_buffer];
311 let wait_sems = [wait_semaphore];
312 let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
313 let wait_values = [0u64];
314
315 let next_val = self
316 .internal_frame_counter
317 .fetch_add(1, std::sync::atomic::Ordering::AcqRel)
318 + 1;
319 let signal_sems = [signal_semaphore, self.internal_frame_semaphore];
320 let signal_values = [0u64, next_val];
321
322 let mut timeline_info = vk::TimelineSemaphoreSubmitInfo::default()
323 .wait_semaphore_values(&wait_values)
324 .signal_semaphore_values(&signal_values);
325
326 let submit_info = vk::SubmitInfo::default()
327 .command_buffers(&cmd_buffers)
328 .wait_semaphores(&wait_sems)
329 .wait_dst_stage_mask(&wait_stages)
330 .signal_semaphores(&signal_sems)
331 .push_next(&mut timeline_info);
332
333 let raw_fence = fence.unwrap_or(vk::Fence::null());
334 {
335 let queue = self.queue.lock().unwrap();
336 self.device
337 .queue_submit(*queue, &[submit_info], raw_fence)
338 .map_err(|e| HalError::CommandError(e.to_string()))?;
339 }
340
341 self.pending_async_recorders
342 .lock()
343 .unwrap()
344 .push((next_val, vec![recorder]));
345
346 Ok(())
347 }
348 }
349}