2 * Copyright (c) 2017-2021 Arm Limited.
4 * SPDX-License-Identifier: MIT
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * @file swapchain_base.hpp
28 * @brief Contains the class definition for a base swapchain.
34 #include <semaphore.h>
35 #include <vulkan/vulkan.h>
38 #include <layer/private_data.hpp>
39 #include <util/timed_semaphore.hpp>
40 #include <util/custom_allocator.hpp>
44 struct swapchain_image
55 /* Implementation specific data */
58 VkImage image{VK_NULL_HANDLE};
59 status status{swapchain_image::INVALID};
61 VkFence present_fence{VK_NULL_HANDLE};
65 * @brief Base swapchain class
67 * - the swapchain implementations inherit from this class.
68 * - the VkSwapchain will hold a pointer to this class.
69 * - much of the swapchain implementation is done by this class, as the only things needed
70 * in the implementation are how to create a presentable image and how to present an image.
75 swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator);
77 virtual ~swapchain_base()
83 * @brief Create swapchain.
85 * Perform all swapchain initialization, create presentable images etc.
87 VkResult init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info);
90 * @brief Acquires a free image.
92 * Current implementation blocks until a free image is available.
94 * @param timeout Unused since we block until a free image is available.
96 * @param semaphore A semaphore signaled once an image is acquired.
98 * @param fence A fence signaled once an image is acquired.
100 * @param pImageIndex The index of the acquired image.
102 * @return VK_SUCCESS on completion.
104 VkResult acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index);
107 * @brief Gets the number of swapchain images or a number of at most
108 * m_num_swapchain_images images.
110 * @param pSwapchainImageCount Used to return number of images in
111 * the swapchain if second parameter is nullptr or represents the
112 * number of images to be returned in second parameter.
114 * @param pSwapchainImage Array of VkImage handles.
116 * @return If number of requested images is less than the number of available
117 * images in the swapchain returns VK_INCOMPLETE otherwise VK_SUCCESS.
119 VkResult get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_image);
122 * @brief Submits a present request for the supplied image.
124 * @param queue The queue to which the submission will be made to.
126 * @param pPresentInfo Information about the swapchain and image to be presented.
128 * @param imageIndex The index of the image to be presented.
130 * @return If queue submission fails returns error of vkQueueSubmit, if the
131 * swapchain has a descendant who started presenting returns VK_ERROR_OUT_OF_DATE_KHR,
132 * otherwise returns VK_SUCCESS.
134 VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index);
137 * @brief Get the allocator
139 * @return const util::allocator The allocator used in the swapchain
141 const util::allocator &get_allocator() const
148 layer::device_private_data &m_device_data;
151 * @brief Handle to the page flip thread.
153 std::thread m_page_flip_thread;
156 * @brief Whether the page flip thread has to continue running or terminate.
158 bool m_page_flip_thread_run;
161 * @brief In case we encounter threading or drm errors we need a way to
162 * notify the user of the failure. When this flag is false, acquire_next_image
163 * will return an error code.
169 /* Ring buffer to hold the image indexes. */
171 /* Head of the ring. */
173 /* End of the ring. */
175 /* Size of the ring. */
179 * @brief A semaphore to be signalled once a page flip event occurs.
181 util::timed_semaphore m_page_flip_semaphore;
184 * @brief A semaphore to be signalled once the swapchain has one frame on screen.
186 sem_t m_start_present_semaphore;
189 * @brief A mutex to protect access to the statuses of the swapchain's images and
190 * any code paths that rely on this information. We use a recursive mutex as some
191 * functions such as 'destroy_image' both change an image's status and are called
192 * conditionally based on an image's status in some cases. A recursive mutex allows
193 * these functions to be called both with and without the mutex already locked in the
196 std::recursive_mutex m_image_status_mutex;
199 * @brief Defines if the pthread_t and sem_t members of the class are defined.
201 * As they are opaque types theer's no known invalid value that we ca initialize to,
202 * and therefore determine if we need to cleanup.
204 bool m_thread_sem_defined;
207 * @brief A flag to track if it is the first present for the chain.
209 bool m_first_present;
212 * @brief In order to present the images in a FIFO order we implement
213 * a ring buffer to hold the images queued for presentation. Since the
214 * two pointers (head and tail) are used by different
215 * threads and we do not allow the application to acquire more images
216 * than we have we eliminate race conditions.
218 ring_buffer m_pending_buffer_pool;
221 * @brief User provided memory allocation callbacks.
223 const util::allocator m_allocator;
226 * @brief Vector of images in the swapchain.
228 util::vector<swapchain_image> m_swapchain_images;
231 * @brief Handle to the surface object this swapchain will present images to.
233 VkSurfaceKHR m_surface;
236 * @brief present mode to use for this swapchain
238 VkPresentModeKHR m_present_mode;
241 * @brief Descendant of this swapchain.
242 * Used to check whether or not a descendant of this swapchain has started
243 * presenting images to the surface already. If it has, any calls to queuePresent
244 * for this swapchain will return VK_ERROR_OUT_OF_DATE_KHR.
246 VkSwapchainKHR m_descendant;
249 * @brief Ancestor of this swapchain.
250 * Used to check whether the ancestor swapchain has completed all of its
251 * pending page flips (this is required before this swapchain presents for the
254 VkSwapchainKHR m_ancestor;
257 * @brief Handle to the logical device the swapchain is created for.
262 * @brief Handle to the queue used for signalling submissions
267 * @brief Return the VkAllocationCallbacks passed in this object constructor.
269 const VkAllocationCallbacks *get_allocation_callbacks()
271 return m_allocator.get_original_callbacks();
275 * @brief Method to wait on all pending buffers to be displayed.
277 void wait_for_pending_buffers();
280 * @brief Remove cached ancestor.
282 void clear_ancestor();
285 * @brief Remove cached descendant.
287 void clear_descendant();
290 * @brief Deprecate this swapchain.
292 * If an application replaces an old swapchain with a new one, the older swapchain
293 * needs to be deprecated. This method releases all the FREE images and sets the
294 * descendant of the swapchain. We do not need to care about images in other states
295 * at this point since they will be released by the page flip thread.
297 * @param descendant Handle to the descendant swapchain.
299 void deprecate(VkSwapchainKHR descendant);
302 * @brief Platform specific initialization
304 virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0;
307 * @brief Base swapchain teardown.
309 * Even though the inheritance gives us a nice way to defer display specific allocation
310 * and presentation outside of the base class, it however robs the children classes - which
311 * also happen to do some of their state setting - the oppurtunity to do the last clean up
312 * call, as the base class' destructor is called at the end. This method provides a way to do it.
313 * The destructor is a virtual function and much of the swapchain teardown happens in this method
314 * which gets called from the child's destructor.
319 * @brief Creates a new swapchain image.
321 * @param image_create_info Data to be used to create the image.
323 * @param image Handle to the image.
325 * @return If image creation is successful returns VK_SUCCESS, otherwise
326 * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED
327 * depending on the error that occured.
329 virtual VkResult create_image(VkImageCreateInfo image_create_info, swapchain_image &image) = 0;
332 * @brief Method to present and image
334 * @param pending_index Index of the pending image to be presented.
337 virtual void present_image(uint32_t pending_index) = 0;
340 * @brief Transition a presented image to free.
342 * Called by swapchain implementation when a new image has been presented.
344 * @param presented_index Index of the image to be marked as free.
346 void unpresent_image(uint32_t presented_index);
349 * @brief Method to release a swapchain image
351 * @param image Handle to the image about to be released.
353 virtual void destroy_image(swapchain_image &image){};
356 * @brief Hook for any actions to free up a buffer for acquire
358 * @param[in,out] timeout time to wait, in nanoseconds. 0 doesn't block,
359 * UINT64_MAX waits indefinately. The timeout should
360 * be updated if a sleep is required - this can
361 * be set to 0 if the semaphore is now not expected
364 virtual VkResult get_free_buffer(uint64_t *timeout)
371 * @brief Wait for a buffer to become free.
373 VkResult wait_for_free_buffer(uint64_t timeout);
376 * @brief A semaphore to be signalled once a free image becomes available.
378 * Uses a custom semaphore implementation that uses a condition variable.
379 * it is slower, but has a safe timedwait implementation.
381 * This is kept private as waiting should be done via wait_for_free_buffer().
383 util::timed_semaphore m_free_image_semaphore;
386 * @brief Per swapchain thread function that handles page flipping.
388 * This thread should be running for the lifetime of the swapchain.
389 * The thread simply calls the implementation's present_image() method.
390 * There are 3 main cases we cover here:
392 * 1. On the first present of the swapchain if the swapchain has
393 * an ancestor we must wait for it to finish presenting.
394 * 2. The normal use case where we do page flipping, in this
395 * case change the currently PRESENTED image with the oldest
397 * 3. If the enqueued image is marked as FREE it means the
398 * descendant of the swapchain has started presenting so we
399 * should release the image and continue.
401 * The function always waits on the page_flip_semaphore of the
402 * swapchain. Once it passes that we must wait for the fence of the
403 * oldest pending image to be signalled, this means that the gpu has
404 * finished rendering to it and we can present it. From there on the
405 * logic splits into the above 3 cases and if an image has been
406 * presented then the old one is marked as FREE and the free_image
407 * semaphore of the swapchain will be posted.
409 void page_flip_thread();
412 } /* namespace wsi */