Fix potential hang on swapchain inheritance
[platform/core/uifw/vulkan-wsi-tizen.git] / wsi / swapchain_base.hpp
1 /*
2  * Copyright (c) 2017-2021 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
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
22  * SOFTWARE.
23  */
24
25 /**
26  * @file swapchain_base.hpp
27  *
28  * @brief Contains the class definition for a base swapchain.
29  */
30
31 #pragma once
32
33 #include <pthread.h>
34 #include <semaphore.h>
35 #include <vulkan/vulkan.h>
36 #include <thread>
37
38 #include <layer/private_data.hpp>
39 #include <util/timed_semaphore.hpp>
40 #include <util/custom_allocator.hpp>
41
42 namespace wsi
43 {
44 struct swapchain_image
45 {
46    enum status
47    {
48       INVALID,
49       ACQUIRED,
50       PENDING,
51       PRESENTED,
52       FREE,
53    };
54
55    /* Implementation specific data */
56    void *data{nullptr};
57
58    VkImage image{VK_NULL_HANDLE};
59    status status{swapchain_image::INVALID};
60
61    VkFence present_fence{VK_NULL_HANDLE};
62 };
63
64 /**
65  * @brief Base swapchain class
66  *
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.
71  */
72 class swapchain_base
73 {
74 public:
75    swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator);
76
77    virtual ~swapchain_base()
78    {
79       /* nop */
80    }
81
82    /**
83     * @brief Create swapchain.
84     *
85     * Perform all swapchain initialization, create presentable images etc.
86     */
87    VkResult init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info);
88
89    /**
90     * @brief Acquires a free image.
91     *
92     * Current implementation blocks until a free image is available.
93     *
94     * @param timeout Unused since we block until a free image is available.
95     *
96     * @param semaphore A semaphore signaled once an image is acquired.
97     *
98     * @param fence A fence signaled once an image is acquired.
99     *
100     * @param pImageIndex The index of the acquired image.
101     *
102     * @return VK_SUCCESS on completion.
103     */
104    VkResult acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index);
105
106    /**
107     * @brief Gets the number of swapchain images or a number of at most
108     * m_num_swapchain_images images.
109     *
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.
113     *
114     * @param pSwapchainImage Array of VkImage handles.
115     *
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.
118     */
119    VkResult get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_image);
120
121    /**
122     * @brief Submits a present request for the supplied image.
123     *
124     * @param queue The queue to which the submission will be made to.
125     *
126     * @param pPresentInfo Information about the swapchain and image to be presented.
127     *
128     * @param imageIndex The index of the image to be presented.
129     *
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.
133     */
134    VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index);
135
136    /**
137     * @brief Get the allocator
138     *
139     * @return const util::allocator The allocator used in the swapchain
140     */
141    const util::allocator &get_allocator() const
142    {
143       return m_allocator;
144    }
145
146 protected:
147
148    layer::device_private_data &m_device_data;
149
150    /**
151     * @brief Handle to the page flip thread.
152     */
153    std::thread m_page_flip_thread;
154
155    /**
156     * @brief Whether the page flip thread has to continue running or terminate.
157     */
158    bool m_page_flip_thread_run;
159
160    /**
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.
164     */
165    bool m_is_valid;
166
167    struct ring_buffer
168    {
169       /* Ring buffer to hold the image indexes. */
170       uint32_t *ring;
171       /* Head of the ring. */
172       uint32_t head;
173       /* End of the ring. */
174       uint32_t tail;
175       /* Size of the ring. */
176       uint32_t size;
177    };
178    /**
179     * @brief A semaphore to be signalled once a page flip event occurs.
180     */
181    util::timed_semaphore m_page_flip_semaphore;
182
183    /**
184     * @brief A semaphore to be signalled once the swapchain has one frame on screen.
185     */
186    sem_t m_start_present_semaphore;
187
188    /**
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
194     * same thread.
195     */
196    std::recursive_mutex m_image_status_mutex;
197
198    /**
199     * @brief Defines if the pthread_t and sem_t members of the class are defined.
200     *
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.
203     */
204    bool m_thread_sem_defined;
205
206    /**
207     * @brief A flag to track if it is the first present for the chain.
208     */
209    bool m_first_present;
210
211    /**
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.
217     */
218    ring_buffer m_pending_buffer_pool;
219
220    /**
221     * @brief User provided memory allocation callbacks.
222     */
223    const util::allocator m_allocator;
224
225    /**
226     * @brief Vector of images in the swapchain.
227     */
228    util::vector<swapchain_image> m_swapchain_images;
229
230    /**
231     * @brief Handle to the surface object this swapchain will present images to.
232     */
233    VkSurfaceKHR m_surface;
234
235    /**
236     * @brief present mode to use for this swapchain
237     */
238    VkPresentModeKHR m_present_mode;
239
240    /**
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.
245     */
246    VkSwapchainKHR m_descendant;
247
248    /**
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
252     * first time.
253     */
254    VkSwapchainKHR m_ancestor;
255
256    /**
257     *  @brief Handle to the logical device the swapchain is created for.
258     */
259    VkDevice m_device;
260
261    /**
262     *  @brief Handle to the queue used for signalling submissions
263     */
264    VkQueue m_queue;
265
266    /**
267     * @brief Return the VkAllocationCallbacks passed in this object constructor.
268     */
269    const VkAllocationCallbacks *get_allocation_callbacks()
270    {
271       return m_allocator.get_original_callbacks();
272    }
273
274    /**
275     * @brief Method to wait on all pending buffers to be displayed.
276     */
277    void wait_for_pending_buffers();
278
279    /**
280     * @brief Remove cached ancestor.
281     */
282    void clear_ancestor();
283
284    /**
285     * @brief Remove cached descendant.
286     */
287    void clear_descendant();
288
289    /**
290     * @brief Deprecate this swapchain.
291     *
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.
296     *
297     * @param descendant Handle to the descendant swapchain.
298     */
299    void deprecate(VkSwapchainKHR descendant);
300
301    /**
302     * @brief Platform specific initialization
303     */
304    virtual VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0;
305
306    /**
307     * @brief Base swapchain teardown.
308     *
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.
315     */
316    void teardown();
317
318    /**
319     * @brief Creates a new swapchain image.
320     *
321     * @param image_create_info Data to be used to create the image.
322     *
323     * @param image Handle to the image.
324     *
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.
328     */
329    virtual VkResult create_image(VkImageCreateInfo image_create_info, swapchain_image &image) = 0;
330
331    /**
332     * @brief Method to present and image
333     *
334     * @param pending_index Index of the pending image to be presented.
335     *
336     */
337    virtual void present_image(uint32_t pending_index) = 0;
338
339    /**
340     * @brief Transition a presented image to free.
341     *
342     * Called by swapchain implementation when a new image has been presented.
343     *
344     * @param presented_index Index of the image to be marked as free.
345     */
346    void unpresent_image(uint32_t presented_index);
347
348    /**
349     * @brief Method to release a swapchain image
350     *
351     * @param image Handle to the image about to be released.
352     */
353    virtual void destroy_image(swapchain_image &image){};
354
355    /**
356     * @brief Hook for any actions to free up a buffer for acquire
357     *
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
362     *                        block.
363     */
364    virtual VkResult get_free_buffer(uint64_t *timeout)
365    {
366       return VK_SUCCESS;
367    }
368
369 private:
370    /**
371     * @brief Wait for a buffer to become free.
372     */
373    VkResult wait_for_free_buffer(uint64_t timeout);
374
375    /**
376     * @brief A semaphore to be signalled once a free image becomes available.
377     *
378     * Uses a custom semaphore implementation that uses a condition variable.
379     * it is slower, but has a safe timedwait implementation.
380     *
381     * This is kept private as waiting should be done via wait_for_free_buffer().
382     */
383    util::timed_semaphore m_free_image_semaphore;
384
385    /**
386     * @brief Per swapchain thread function that handles page flipping.
387     *
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:
391     *
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
396     *    PENDING image.
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.
400     *
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.
408     **/
409    void page_flip_thread();
410 };
411
412 } /* namespace wsi */