vulkano_util/
renderer.rs

1use crate::{context::VulkanoContext, window::WindowDescriptor};
2use foldhash::HashMap;
3use std::{sync::Arc, time::Duration};
4use vulkano::{
5    device::{Device, Queue},
6    format::Format,
7    image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
8    memory::allocator::{AllocationCreateInfo, StandardMemoryAllocator},
9    swapchain::{self, PresentMode, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo},
10    sync::{self, GpuFuture},
11    Validated, VulkanError,
12};
13use winit::window::Window;
14
15/// Most common image format
16pub const DEFAULT_IMAGE_FORMAT: Format = Format::R8G8B8A8_UNORM;
17
18/// A window renderer struct holding the winit window surface and functionality for organizing your
19/// render between frames.
20///
21/// Begin rendering with [`VulkanoWindowRenderer::acquire`] and finish with
22/// [`VulkanoWindowRenderer::present`]. Between those, you should execute your command buffers.
23///
24/// The intended usage of this struct is through [`crate::window::VulkanoWindows`].
25pub struct VulkanoWindowRenderer {
26    window: Arc<Window>,
27    graphics_queue: Arc<Queue>,
28    compute_queue: Arc<Queue>,
29    swapchain: Arc<Swapchain>,
30    final_views: Vec<Arc<ImageView>>,
31    memory_allocator: Arc<StandardMemoryAllocator>,
32    /// Additional image views that you can add which are resized with the window.
33    /// Use associated functions to get access to these.
34    additional_image_views: HashMap<usize, Arc<ImageView>>,
35    recreate_swapchain: bool,
36    previous_frame_end: Option<Box<dyn GpuFuture>>,
37    image_index: u32,
38    present_mode: PresentMode,
39}
40
41impl VulkanoWindowRenderer {
42    /// Creates a new [`VulkanoWindowRenderer`] which is used to orchestrate your rendering with
43    /// Vulkano. Pass [`WindowDescriptor`] and optionally a function modifying the
44    /// [`SwapchainCreateInfo`] parameters.
45    pub fn new(
46        vulkano_context: &VulkanoContext,
47        window: Window,
48        descriptor: &WindowDescriptor,
49        swapchain_create_info_modify: fn(&mut SwapchainCreateInfo),
50    ) -> VulkanoWindowRenderer {
51        let window = Arc::new(window);
52
53        // Create swap chain & frame(s) to which we'll render
54        let (swap_chain, final_views) = Self::create_swapchain(
55            vulkano_context.device().clone(),
56            &window,
57            descriptor,
58            swapchain_create_info_modify,
59        );
60
61        let previous_frame_end = Some(sync::now(vulkano_context.device().clone()).boxed());
62
63        VulkanoWindowRenderer {
64            window,
65            graphics_queue: vulkano_context.graphics_queue().clone(),
66            compute_queue: vulkano_context.compute_queue().clone(),
67            swapchain: swap_chain,
68            final_views,
69            memory_allocator: vulkano_context.memory_allocator().clone(),
70            additional_image_views: HashMap::default(),
71            recreate_swapchain: false,
72            previous_frame_end,
73            image_index: 0,
74            present_mode: descriptor.present_mode,
75        }
76    }
77
78    /// Creates the swapchain and its images based on [`WindowDescriptor`]. The swapchain creation
79    /// can be modified with the `swapchain_create_info_modify` function passed as an input.
80    fn create_swapchain(
81        device: Arc<Device>,
82        window: &Arc<Window>,
83        window_descriptor: &WindowDescriptor,
84        swapchain_create_info_modify: fn(&mut SwapchainCreateInfo),
85    ) -> (Arc<Swapchain>, Vec<Arc<ImageView>>) {
86        let surface = Surface::from_window(device.instance().clone(), window.clone()).unwrap();
87        let surface_capabilities = device
88            .physical_device()
89            .surface_capabilities(&surface, Default::default())
90            .unwrap();
91        let image_format = device
92            .physical_device()
93            .surface_formats(&surface, Default::default())
94            .unwrap()[0]
95            .0;
96        let (swapchain, images) = Swapchain::new(device, surface, {
97            let mut create_info = SwapchainCreateInfo {
98                min_image_count: surface_capabilities.min_image_count.max(2),
99                image_format,
100                image_extent: window.inner_size().into(),
101                image_usage: ImageUsage::COLOR_ATTACHMENT,
102                composite_alpha: surface_capabilities
103                    .supported_composite_alpha
104                    .into_iter()
105                    .next()
106                    .unwrap(),
107                ..Default::default()
108            };
109            // Get present mode from window descriptor
110            create_info.present_mode = window_descriptor.present_mode;
111            swapchain_create_info_modify(&mut create_info);
112            create_info
113        })
114        .unwrap();
115        let images = images
116            .into_iter()
117            .map(|image| ImageView::new_default(image).unwrap())
118            .collect::<Vec<_>>();
119
120        (swapchain, images)
121    }
122
123    /// Set window renderer present mode. This triggers a swapchain recreation.
124    #[inline]
125    pub fn set_present_mode(&mut self, present_mode: PresentMode) {
126        if self.present_mode != present_mode {
127            self.present_mode = present_mode;
128            self.recreate_swapchain = true;
129        }
130    }
131
132    /// Return swapchain image format.
133    #[inline]
134    pub fn swapchain_format(&self) -> Format {
135        self.final_views[self.image_index as usize].format()
136    }
137
138    /// Returns the index of last swapchain image that is the next render target.
139    #[inline]
140    pub fn image_index(&self) -> u32 {
141        self.image_index
142    }
143
144    /// Graphics queue of this window. You also can access this through [`VulkanoContext`].
145    #[inline]
146    pub fn graphics_queue(&self) -> Arc<Queue> {
147        self.graphics_queue.clone()
148    }
149
150    /// Compute queue of this window. You can also access this through [`VulkanoContext`].
151    #[inline]
152    pub fn compute_queue(&self) -> Arc<Queue> {
153        self.compute_queue.clone()
154    }
155
156    /// Render target surface.
157    #[inline]
158    pub fn surface(&self) -> Arc<Surface> {
159        self.swapchain.surface().clone()
160    }
161
162    /// Winit window (you can manipulate the window through this).
163    #[inline]
164    pub fn window(&self) -> &Window {
165        &self.window
166    }
167
168    /// Size of the physical window.
169    #[inline]
170    pub fn window_size(&self) -> [f32; 2] {
171        let size = self.window().inner_size();
172        [size.width as f32, size.height as f32]
173    }
174
175    /// Size of the final swapchain image (surface).
176    #[inline]
177    pub fn swapchain_image_size(&self) -> [u32; 2] {
178        self.final_views[0].image().extent()[0..2]
179            .try_into()
180            .unwrap()
181    }
182
183    /// Return the current swapchain image view.
184    #[inline]
185    pub fn swapchain_image_view(&self) -> Arc<ImageView> {
186        self.final_views[self.image_index as usize].clone()
187    }
188
189    /// Return scale factor accounted window size.
190    #[inline]
191    pub fn resolution(&self) -> [f32; 2] {
192        let size = self.window().inner_size();
193        let scale_factor = self.window().scale_factor();
194        [
195            (size.width as f64 / scale_factor) as f32,
196            (size.height as f64 / scale_factor) as f32,
197        ]
198    }
199
200    #[inline]
201    pub fn aspect_ratio(&self) -> f32 {
202        let dims = self.window_size();
203        dims[0] / dims[1]
204    }
205
206    /// Returns a reference to the swapchain image views.
207    #[inline]
208    #[must_use]
209    // swapchain_image_views or swapchain_images_views, neither sounds good.
210    pub fn swapchain_image_views(&self) -> &[Arc<ImageView>] {
211        // Why do we use "final views" as the field name,
212        // yet always externally refer to them as "swapchain image views"?
213        &self.final_views
214    }
215
216    /// Resize swapchain and camera view images at the beginning of next frame based on window
217    /// size.
218    #[inline]
219    pub fn resize(&mut self) {
220        self.recreate_swapchain = true;
221    }
222
223    /// Add interim image view that resizes with window.
224    #[inline]
225    pub fn add_additional_image_view(&mut self, key: usize, format: Format, usage: ImageUsage) {
226        let final_view_image = self.final_views[0].image();
227        let image = ImageView::new_default(
228            Image::new(
229                self.memory_allocator.clone(),
230                ImageCreateInfo {
231                    image_type: ImageType::Dim2d,
232                    format,
233                    extent: final_view_image.extent(),
234                    usage,
235                    ..Default::default()
236                },
237                AllocationCreateInfo::default(),
238            )
239            .unwrap(),
240        )
241        .unwrap();
242        self.additional_image_views.insert(key, image);
243    }
244
245    /// Get additional image view by key.
246    #[inline]
247    pub fn get_additional_image_view(&mut self, key: usize) -> Arc<ImageView> {
248        self.additional_image_views.get(&key).unwrap().clone()
249    }
250
251    /// Remove additional image by key.
252    #[inline]
253    pub fn remove_additional_image_view(&mut self, key: usize) {
254        self.additional_image_views.remove(&key);
255    }
256
257    /// Begin your rendering by calling `acquire`.
258    /// 'on_recreate_swapchain' is called when the swapchain gets recreated, due to being resized,
259    /// suboptimal, or changing the present mode. Returns a [`GpuFuture`] representing the time
260    /// after which the swapchain image has been acquired and previous frame ended.
261    /// Execute your command buffers after calling this function and
262    /// finish rendering by calling [`VulkanoWindowRenderer::present`].
263    #[inline]
264    pub fn acquire(
265        &mut self,
266        timeout: Option<Duration>,
267        on_recreate_swapchain: impl FnOnce(&[Arc<ImageView>]),
268    ) -> Result<Box<dyn GpuFuture>, VulkanError> {
269        // Recreate swap chain if needed (when resizing of window occurs or swapchain is outdated)
270        // Also resize render views if needed
271        if self.recreate_swapchain {
272            self.recreate_swapchain_and_views();
273            on_recreate_swapchain(&self.final_views);
274        }
275
276        // Acquire next image in the swapchain
277        let (image_index, suboptimal, acquire_future) =
278            match swapchain::acquire_next_image(self.swapchain.clone(), timeout)
279                .map_err(Validated::unwrap)
280            {
281                Ok(r) => r,
282                Err(VulkanError::OutOfDate) => {
283                    self.recreate_swapchain = true;
284                    return Err(VulkanError::OutOfDate);
285                }
286                Err(e) => panic!("failed to acquire next image: {e}"),
287            };
288        if suboptimal {
289            self.recreate_swapchain = true;
290        }
291        // Update our image index
292        self.image_index = image_index;
293
294        let future = self.previous_frame_end.take().unwrap().join(acquire_future);
295
296        Ok(future.boxed())
297    }
298
299    /// Finishes rendering by presenting the swapchain. Pass your last future as an input to this
300    /// function.
301    ///
302    /// Depending on your implementation, you may want to wait on your future. For example, a
303    /// compute shader dispatch using an image that's being later drawn should probably be waited
304    /// on.
305    #[inline]
306    pub fn present(&mut self, after_future: Box<dyn GpuFuture>, wait_future: bool) {
307        let future = after_future
308            .then_swapchain_present(
309                self.graphics_queue.clone(),
310                SwapchainPresentInfo::swapchain_image_index(
311                    self.swapchain.clone(),
312                    self.image_index,
313                ),
314            )
315            .then_signal_fence_and_flush();
316        match future.map_err(Validated::unwrap) {
317            Ok(mut future) => {
318                if wait_future {
319                    future.wait(None).unwrap_or_else(|e| println!("{e}"))
320                    // wait allows you to organize resource waiting yourself.
321                } else {
322                    future.cleanup_finished();
323                }
324
325                self.previous_frame_end = Some(future.boxed());
326            }
327            Err(VulkanError::OutOfDate) => {
328                self.recreate_swapchain = true;
329                self.previous_frame_end =
330                    Some(sync::now(self.graphics_queue.device().clone()).boxed());
331            }
332            Err(e) => {
333                println!("failed to flush future: {e}");
334                self.previous_frame_end =
335                    Some(sync::now(self.graphics_queue.device().clone()).boxed());
336            }
337        }
338    }
339
340    /// Recreates swapchain images and image views which follow the window size.
341    fn recreate_swapchain_and_views(&mut self) {
342        let image_extent: [u32; 2] = self.window().inner_size().into();
343
344        if image_extent.contains(&0) {
345            return;
346        }
347
348        let (new_swapchain, new_images) = self
349            .swapchain
350            .recreate(SwapchainCreateInfo {
351                image_extent,
352                // Use present mode from current state
353                present_mode: self.present_mode,
354                ..self.swapchain.create_info()
355            })
356            .expect("failed to recreate swapchain");
357
358        self.swapchain = new_swapchain;
359        let new_images = new_images
360            .into_iter()
361            .map(|image| ImageView::new_default(image).unwrap())
362            .collect::<Vec<_>>();
363        self.final_views = new_images;
364        // Resize images that follow swapchain size
365        let resizable_views = self
366            .additional_image_views
367            .iter()
368            .map(|c| *c.0)
369            .collect::<Vec<usize>>();
370        for i in resizable_views {
371            let format = self.get_additional_image_view(i).format();
372            let usage = self.get_additional_image_view(i).usage();
373            self.remove_additional_image_view(i);
374            self.add_additional_image_view(i, format, usage);
375        }
376        self.recreate_swapchain = false;
377    }
378}