util: add class for storing extensions strings
[platform/core/uifw/vulkan-wsi-tizen.git] / wsi / swapchain_base.cpp
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.cpp
27  *
28  * @brief Contains the implementation for the swapchain.
29  *
30  * This file contains much of the swapchain implementation,
31  * that is not specific to how images are created or presented.
32  */
33
34 #include <array>
35 #include <cassert>
36 #include <cerrno>
37 #include <cstdio>
38 #include <cstdlib>
39
40 #include <unistd.h>
41 #include <vulkan/vulkan.h>
42
43 #include "swapchain_base.hpp"
44
45 #if VULKAN_WSI_DEBUG > 0
46 #define WSI_PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__)
47 #else
48 #define WSI_PRINT_ERROR(...) (void)0
49 #endif
50
51 namespace wsi
52 {
53
54 void swapchain_base::page_flip_thread()
55 {
56    auto &sc_images = m_swapchain_images;
57    VkResult vk_res = VK_SUCCESS;
58    uint64_t timeout = UINT64_MAX;
59    constexpr uint64_t SEMAPHORE_TIMEOUT = 250000000; /* 250 ms. */
60
61    /* No mutex is needed for the accesses to m_page_flip_thread_run variable as after the variable is
62     * initialized it is only ever changed to false. The while loop will make the thread read the
63     * value repeatedly, and the combination of semaphores and thread joins will force any changes to
64     * the variable to be visible to this thread.
65     */
66    while (m_page_flip_thread_run)
67    {
68       /* Waiting for the page_flip_semaphore which will be signalled once there is an
69        * image to display.*/
70       if ((vk_res = m_page_flip_semaphore.wait(SEMAPHORE_TIMEOUT)) == VK_TIMEOUT)
71       {
72          /* Image is not ready yet. */
73          continue;
74       }
75       assert(vk_res == VK_SUCCESS);
76
77       /* We want to present the oldest queued for present image from our present queue,
78        * which we can find at the sc->pending_buffer_pool.head index. */
79       uint32_t pending_index = m_pending_buffer_pool.ring[m_pending_buffer_pool.head];
80       m_pending_buffer_pool.head = (m_pending_buffer_pool.head + 1) % m_pending_buffer_pool.size;
81
82       /* We wait for the fence of the oldest pending image to be signalled. */
83       vk_res = m_device_data.disp.WaitForFences(m_device, 1, &sc_images[pending_index].present_fence, VK_TRUE,
84                                                     timeout);
85       if (vk_res != VK_SUCCESS)
86       {
87          m_is_valid = false;
88          m_free_image_semaphore.post();
89          continue;
90       }
91
92       /* If the descendant has started presenting the queue_present operation has marked the image
93        * as FREE so we simply release it and continue. */
94       if (sc_images[pending_index].status == swapchain_image::FREE)
95       {
96          destroy_image(sc_images[pending_index]);
97          m_free_image_semaphore.post();
98          continue;
99       }
100
101       /* First present of the swapchain. If it has an ancestor, wait until all the pending buffers
102        * from the ancestor have finished page flipping before we set mode. */
103       if (m_first_present)
104       {
105          if (m_ancestor != VK_NULL_HANDLE)
106          {
107             auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
108             ancestor->wait_for_pending_buffers();
109          }
110
111          sem_post(&m_start_present_semaphore);
112
113          present_image(pending_index);
114
115          m_first_present = false;
116       }
117       /* The swapchain has already started presenting. */
118       else
119       {
120          present_image(pending_index);
121       }
122    }
123 }
124
125 void swapchain_base::unpresent_image(uint32_t presented_index)
126 {
127    m_swapchain_images[presented_index].status = swapchain_image::FREE;
128
129    if (m_descendant != VK_NULL_HANDLE)
130    {
131       destroy_image(m_swapchain_images[presented_index]);
132    }
133
134    m_free_image_semaphore.post();
135 }
136
137 swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *callbacks)
138    : m_device_data(dev_data)
139    , m_page_flip_thread_run(true)
140    , m_thread_sem_defined(false)
141    , m_first_present(true)
142    , m_pending_buffer_pool{ nullptr, 0, 0, 0 }
143    , m_allocator(callbacks, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)
144    , m_swapchain_images(m_allocator)
145    , m_surface(VK_NULL_HANDLE)
146    , m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR)
147    , m_descendant(VK_NULL_HANDLE)
148    , m_ancestor(VK_NULL_HANDLE)
149    , m_device(VK_NULL_HANDLE)
150    , m_queue(VK_NULL_HANDLE)
151 {
152 }
153
154 VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info)
155 {
156    assert(device != VK_NULL_HANDLE);
157    assert(swapchain_create_info != nullptr);
158    assert(swapchain_create_info->surface != VK_NULL_HANDLE);
159
160    int res;
161    VkResult result;
162
163    m_device = device;
164    m_surface = swapchain_create_info->surface;
165
166    /* Check presentMode has a compatible value with swapchain - everything else should be taken care at image creation.*/
167    static const std::array<VkPresentModeKHR, 2> present_modes = { VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR };
168    bool present_mode_found = false;
169    for (uint32_t i = 0; i < present_modes.size() && !present_mode_found; i++)
170    {
171       if (swapchain_create_info->presentMode == present_modes[i])
172       {
173          present_mode_found = true;
174       }
175    }
176
177    if (!present_mode_found)
178    {
179       return VK_ERROR_INITIALIZATION_FAILED;
180    }
181
182    /* Init image to invalid values. */
183    if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount))
184       return VK_ERROR_OUT_OF_HOST_MEMORY;
185
186    /* Initialize ring buffer. */
187    m_pending_buffer_pool.ring = m_allocator.create<uint32_t>(m_swapchain_images.size(), 0);
188    if (m_pending_buffer_pool.ring == nullptr)
189    {
190       return VK_ERROR_OUT_OF_HOST_MEMORY;
191    }
192
193    m_pending_buffer_pool.head = 0;
194    m_pending_buffer_pool.tail = 0;
195    m_pending_buffer_pool.size = m_swapchain_images.size();
196
197    /* We have allocated images, we can call the platform init function if something needs to be done. */
198    result = init_platform(device, swapchain_create_info);
199    if (result != VK_SUCCESS)
200    {
201       return result;
202    }
203
204    VkImageCreateInfo image_create_info = {};
205    image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
206    image_create_info.pNext = nullptr;
207    image_create_info.imageType = VK_IMAGE_TYPE_2D;
208    image_create_info.format = swapchain_create_info->imageFormat;
209    image_create_info.extent = { swapchain_create_info->imageExtent.width, swapchain_create_info->imageExtent.height, 1 };
210    image_create_info.mipLevels = 1;
211    image_create_info.arrayLayers = swapchain_create_info->imageArrayLayers;
212    image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
213    image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
214    image_create_info.usage = swapchain_create_info->imageUsage;
215    image_create_info.flags = 0;
216    image_create_info.sharingMode = swapchain_create_info->imageSharingMode;
217    image_create_info.queueFamilyIndexCount = swapchain_create_info->queueFamilyIndexCount;
218    image_create_info.pQueueFamilyIndices = swapchain_create_info->pQueueFamilyIndices;
219    image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
220
221    result = m_free_image_semaphore.init(m_swapchain_images.size());
222    if (result != VK_SUCCESS)
223    {
224       assert(result == VK_ERROR_OUT_OF_HOST_MEMORY);
225       return result;
226    }
227
228    for (auto& img : m_swapchain_images)
229    {
230       result = create_image(image_create_info, img);
231       if (result != VK_SUCCESS)
232       {
233          return result;
234       }
235    }
236
237    m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);
238    result = m_device_data.SetDeviceLoaderData(m_device, m_queue);
239    if (VK_SUCCESS != result)
240    {
241       return result;
242    }
243
244    /* Setup semaphore for signaling pageflip thread */
245    result = m_page_flip_semaphore.init(0);
246    if (result != VK_SUCCESS)
247    {
248       return result;
249    }
250
251    res = sem_init(&m_start_present_semaphore, 0, 0);
252    /* Only programming error can cause this to fail. */
253    assert(res == 0);
254    if (res != 0)
255    {
256       return VK_ERROR_OUT_OF_HOST_MEMORY;
257    }
258
259    m_thread_sem_defined = true;
260
261    /* Launch page flipping thread */
262    m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this);
263
264    /* Release the swapchain images of the old swapchain in order
265     * to free up memory for new swapchain. This is necessary especially
266     * on platform with limited display memory size.
267     *
268     * NB: This must be done last in initialization, when the rest of
269     * the swapchain is valid.
270     */
271    if (swapchain_create_info->oldSwapchain != VK_NULL_HANDLE)
272    {
273       /* Set ancestor. */
274       m_ancestor = swapchain_create_info->oldSwapchain;
275
276       auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);
277       ancestor->deprecate(reinterpret_cast<VkSwapchainKHR>(this));
278    }
279
280    m_is_valid = true;
281
282    return VK_SUCCESS;
283 }
284
285 void swapchain_base::teardown()
286 {
287    /* This method will block until all resources associated with this swapchain
288     * are released. Images in the ACQUIRED or FREE state can be freed
289     * immediately. For images in the PRESENTED state, we will block until the
290     * presentation engine is finished with them. */
291
292    int res;
293    bool descendent_started_presenting = false;
294
295    if (m_descendant != VK_NULL_HANDLE)
296    {
297       auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
298       for (auto& img : desc->m_swapchain_images)
299       {
300          if (img.status == swapchain_image::PRESENTED ||
301              img.status == swapchain_image::PENDING)
302          {
303             /* Here we wait for the start_present_semaphore, once this semaphore is up,
304              * the descendant has finished waiting, we don't want to delete vkImages and vkFences
305              * and semaphores before the waiting is done. */
306             sem_wait(&desc->m_start_present_semaphore);
307
308             descendent_started_presenting = true;
309             break;
310          }
311       }
312    }
313
314    /* If descendant started presenting, there is no pending buffer in the swapchain. */
315    if (m_is_valid && descendent_started_presenting == false)
316    {
317       wait_for_pending_buffers();
318    }
319
320    if (m_queue != VK_NULL_HANDLE)
321    {
322       /* Make sure the vkFences are done signaling. */
323       m_device_data.disp.QueueWaitIdle(m_queue);
324    }
325
326    /* We are safe to destroy everything. */
327    if (m_thread_sem_defined)
328    {
329       /* Tell flip thread to end. */
330       m_page_flip_thread_run = false;
331
332       if (m_page_flip_thread.joinable())
333       {
334          m_page_flip_thread.join();
335       }
336       else
337       {
338          WSI_PRINT_ERROR("m_page_flip_thread is not joinable");
339       }
340
341       res = sem_destroy(&m_start_present_semaphore);
342       if (res != 0)
343       {
344          WSI_PRINT_ERROR("sem_destroy failed for start_present_semaphore with %d\n", errno);
345       }
346    }
347
348    if (m_descendant != VK_NULL_HANDLE)
349    {
350       auto *sc = reinterpret_cast<swapchain_base *>(m_descendant);
351       sc->clear_ancestor();
352    }
353
354    if (m_ancestor != VK_NULL_HANDLE)
355    {
356       auto *sc = reinterpret_cast<swapchain_base *>(m_ancestor);
357       sc->clear_descendant();
358    }
359    /* Release the images array. */
360    for (auto& img : m_swapchain_images)
361    {
362       /* Call implementation specific release */
363       destroy_image(img);
364    }
365
366    m_allocator.destroy(m_swapchain_images.size(), m_pending_buffer_pool.ring);
367 }
368
369 VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index)
370 {
371    VkResult retval = wait_for_free_buffer(timeout);
372    if (retval != VK_SUCCESS)
373    {
374       return retval;
375    }
376
377    if (!m_is_valid)
378    {
379       return VK_ERROR_OUT_OF_HOST_MEMORY;
380    }
381
382    uint32_t i;
383    for (i = 0; i < m_swapchain_images.size(); ++i)
384    {
385       if (m_swapchain_images[i].status == swapchain_image::FREE)
386       {
387          m_swapchain_images[i].status = swapchain_image::ACQUIRED;
388          *image_index = i;
389          break;
390       }
391    }
392
393    assert(i < m_swapchain_images.size());
394
395    if (VK_NULL_HANDLE != semaphore || VK_NULL_HANDLE != fence)
396    {
397       VkSubmitInfo submit = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
398
399       if (VK_NULL_HANDLE != semaphore)
400       {
401          submit.signalSemaphoreCount = 1;
402          submit.pSignalSemaphores = &semaphore;
403       }
404
405       submit.commandBufferCount = 0;
406       submit.pCommandBuffers = nullptr;
407       retval = m_device_data.disp.QueueSubmit(m_queue, 1, &submit, fence);
408       assert(retval == VK_SUCCESS);
409    }
410
411    return retval;
412 }
413
414 VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_images)
415 {
416    if (swapchain_images == nullptr)
417    {
418       /* Return the number of swapchain images. */
419       *swapchain_image_count = m_swapchain_images.size();
420
421       return VK_SUCCESS;
422    }
423    else
424    {
425       assert(m_swapchain_images.size() > 0);
426       assert(*swapchain_image_count > 0);
427
428       /* Populate array, write actual number of images returned. */
429       uint32_t current_image = 0;
430
431       do
432       {
433          swapchain_images[current_image] = m_swapchain_images[current_image].image;
434
435          current_image++;
436
437          if (current_image == m_swapchain_images.size())
438          {
439             *swapchain_image_count = current_image;
440
441             return VK_SUCCESS;
442          }
443
444       } while (current_image < *swapchain_image_count);
445
446       /* If swapchain_image_count is smaller than the number of presentable images
447        * in the swapchain, VK_INCOMPLETE must be returned instead of VK_SUCCESS. */
448       *swapchain_image_count = current_image;
449
450       return VK_INCOMPLETE;
451    }
452 }
453
454 VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index)
455 {
456    VkResult result;
457    bool descendent_started_presenting = false;
458
459    if (m_descendant != VK_NULL_HANDLE)
460    {
461       auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);
462       for (auto& img : desc->m_swapchain_images)
463       {
464          if (img.status == swapchain_image::PRESENTED ||
465              img.status == swapchain_image::PENDING)
466          {
467             descendent_started_presenting = true;
468             break;
469          }
470       }
471    }
472
473    /* When the semaphore that comes in is signalled, we know that all work is done. So, we do not
474     * want to block any future Vulkan queue work on it. So, we pass in BOTTOM_OF_PIPE bit as the
475     * wait flag.
476     */
477    VkPipelineStageFlags pipeline_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
478
479    VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO,
480                                NULL,
481                                present_info->waitSemaphoreCount,
482                                present_info->pWaitSemaphores,
483                                &pipeline_stage_flags,
484                                0,
485                                NULL,
486                                0,
487                                NULL };
488
489    assert(m_swapchain_images[image_index].status == swapchain_image::ACQUIRED);
490    result = m_device_data.disp.ResetFences(m_device, 1, &m_swapchain_images[image_index].present_fence);
491    if (result != VK_SUCCESS)
492    {
493       return result;
494    }
495
496    result = m_device_data.disp.QueueSubmit(queue, 1, &submit_info, m_swapchain_images[image_index].present_fence);
497    if (result != VK_SUCCESS)
498    {
499       return result;
500    }
501
502    /* If the descendant has started presenting, we should release the image
503     * however we do not want to block inside the main thread so we mark it
504     * as free and let the page flip thread take care of it. */
505    if (descendent_started_presenting)
506    {
507       m_swapchain_images[image_index].status = swapchain_image::FREE;
508
509       m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;
510       m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;
511
512       m_page_flip_semaphore.post();
513
514       return VK_ERROR_OUT_OF_DATE_KHR;
515    }
516
517    m_swapchain_images[image_index].status = swapchain_image::PENDING;
518
519    m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;
520    m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;
521
522    m_page_flip_semaphore.post();
523    return VK_SUCCESS;
524 }
525
526 void swapchain_base::deprecate(VkSwapchainKHR descendant)
527 {
528    for (auto& img : m_swapchain_images)
529    {
530       if (img.status == swapchain_image::FREE)
531       {
532          destroy_image(img);
533       }
534    }
535
536    /* Set its descendant. */
537    m_descendant = descendant;
538 }
539
540 void swapchain_base::wait_for_pending_buffers()
541 {
542    int num_acquired_images = 0;
543    int wait;
544
545    for (auto& img : m_swapchain_images)
546    {
547       if (img.status == swapchain_image::ACQUIRED)
548       {
549          ++num_acquired_images;
550       }
551    }
552
553    /* Once all the pending buffers are flipped, the swapchain should have images
554     * in ACQUIRED (application fails to queue them back for presentation), FREE
555     * and one and only one in PRESENTED. */
556    wait = m_swapchain_images.size() - num_acquired_images - 1;
557
558    while (wait > 0)
559    {
560       /* Take down one free image semaphore. */
561       wait_for_free_buffer(UINT64_MAX);
562       --wait;
563    }
564 }
565
566 void swapchain_base::clear_ancestor()
567 {
568    m_ancestor = VK_NULL_HANDLE;
569 }
570
571 void swapchain_base::clear_descendant()
572 {
573    m_descendant = VK_NULL_HANDLE;
574 }
575
576 VkResult swapchain_base::wait_for_free_buffer(uint64_t timeout)
577 {
578    VkResult retval;
579    /* first see if a buffer is already marked as free */
580    retval = m_free_image_semaphore.wait(0);
581    if (retval == VK_NOT_READY)
582    {
583       /* if not, we still have work to do even if timeout==0 -
584        * the swapchain implementation may be able to get a buffer without
585        * waiting */
586
587       retval = get_free_buffer(&timeout);
588       if (retval == VK_SUCCESS)
589       {
590          /* the sub-implementation has done it's thing, so re-check the
591           * semaphore */
592          retval = m_free_image_semaphore.wait(timeout);
593       }
594    }
595
596    return retval;
597 }
598
599 #undef WSI_PRINT_ERROR
600
601 } /* namespace wsi */