diff --git a/util/optional.hpp b/util/optional.hpp index e21e6fd..7af2ce8 100644 --- a/util/optional.hpp +++ b/util/optional.hpp @@ -100,6 +100,26 @@ public: void reset() noexcept { m_has_value = false; + m_value = T{}; + } + + /** + * @brief Reassign/assign the value in the optional. + * + * @return optional& This optional object with the value. + */ + optional &set(T &&val) noexcept + { + m_value = std::move(val); + m_has_value = true; + return *this; + } + + optional &set(const T &val) noexcept + { + m_value = val; + m_has_value = true; + return *this; } /** diff --git a/util/ring_buffer.hpp b/util/ring_buffer.hpp new file mode 100644 index 0000000..56c2351 --- /dev/null +++ b/util/ring_buffer.hpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021 Arm Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "optional.hpp" + +namespace util +{ + +template +class ring_buffer +{ +public: + /** + * @brief Return maximum capacity of the ring buffer. + */ + constexpr std::size_t capacity() + { + return N; + } + + /** + * @brief Return current size of the ring buffer. + */ + std::size_t size() const + { + return m_size; + } + + /** + * @brief Places item into next slot of the ring buffer. + * @return Boolean to indicate success or failure. + */ + template + bool push_back(U &&item) + { + if (size() == capacity()) + { + return false; + } + + m_data[(m_begin + m_size) % N].set(std::forward(item)); + ++m_size; + + return true; + } + + /** + * @brief Gets a pointer to the item at the starting index of the ring buffer. + */ + T *front() + { + return get(m_begin); + } + + /** + * @brief Gets a pointer to the item that was last placed into the ring buffer. + */ + T *back() + { + return get((m_begin + m_size + N - 1) % N); + } + + /** + * @brief Pop the front of the ring buffer. + * + * Item at the starting index of the ring buffer is returned. The slot is subsequently emptied. The starting index of + * the ring buffer increments by 1. + * + * @return Item wrapped in an optional. + */ + util::optional pop_front() + { + if (size() == 0) + { + return util::optional{}; + } + + util::optional value = std::move(m_data[m_begin]); + + m_begin = (m_begin + 1) % N; + --m_size; + + return value; + } + +private: + T *get(std::size_t index) + { + if (m_data[index].has_value()) + { + return std::addressof(m_data[index].value()); + } + else + { + return nullptr; + } + } + + std::array, N> m_data{}; + + // Marks the start index of the ring buffer. + std::size_t m_begin{}; + + // The number of entries in the ring buffer from the start index. + std::size_t m_size{}; +}; + +} /* namespace util */ \ No newline at end of file diff --git a/wsi/headless/surface_properties.cpp b/wsi/headless/surface_properties.cpp index da63ef0..663e516 100644 --- a/wsi/headless/surface_properties.cpp +++ b/wsi/headless/surface_properties.cpp @@ -57,8 +57,7 @@ VkResult surface_properties::get_surface_capabilities(VkPhysicalDevice physical_ UNUSED(surface); /* Image count limits */ surface_capabilities->minImageCount = 1; - /* There is no maximum theoretically speaking */ - surface_capabilities->maxImageCount = UINT32_MAX; + surface_capabilities->maxImageCount = MAX_SWAPCHAIN_IMAGE_COUNT; /* Surface extents */ surface_capabilities->currentExtent = { 0xffffffff, 0xffffffff }; diff --git a/wsi/surface_properties.hpp b/wsi/surface_properties.hpp index b9d032f..ca97fbb 100644 --- a/wsi/surface_properties.hpp +++ b/wsi/surface_properties.hpp @@ -75,6 +75,9 @@ public: * At least the specific VkSurface creation entrypoint must be intercepted. */ virtual PFN_vkVoidFunction get_proc_addr(const char *name) = 0; + + /* There is no maximum theoretically speaking however we choose 3 for practicality */ + static constexpr uint32_t MAX_SWAPCHAIN_IMAGE_COUNT = 3; }; } /* namespace wsi */ diff --git a/wsi/swapchain_base.cpp b/wsi/swapchain_base.cpp index 1c60feb..3576946 100644 --- a/wsi/swapchain_base.cpp +++ b/wsi/swapchain_base.cpp @@ -71,11 +71,11 @@ void swapchain_base::page_flip_thread() /* We want to present the oldest queued for present image from our present queue, * which we can find at the sc->pending_buffer_pool.head index. */ - uint32_t pending_index = m_pending_buffer_pool.ring[m_pending_buffer_pool.head]; - m_pending_buffer_pool.head = (m_pending_buffer_pool.head + 1) % m_pending_buffer_pool.size; + auto pending_index = m_pending_buffer_pool.pop_front(); + assert(pending_index.has_value()); /* We wait for the fence of the oldest pending image to be signalled. */ - vk_res = m_device_data.disp.WaitForFences(m_device, 1, &sc_images[pending_index].present_fence, VK_TRUE, + vk_res = m_device_data.disp.WaitForFences(m_device, 1, &sc_images[*pending_index].present_fence, VK_TRUE, timeout); if (vk_res != VK_SUCCESS) { @@ -88,9 +88,9 @@ void swapchain_base::page_flip_thread() /* If the descendant has started presenting the queue_present operation has marked the image * as FREE so we simply release it and continue. */ - if (sc_images[pending_index].status == swapchain_image::FREE) + if (sc_images[*pending_index].status == swapchain_image::FREE) { - destroy_image(sc_images[pending_index]); + destroy_image(sc_images[*pending_index]); m_free_image_semaphore.post(); continue; } @@ -108,14 +108,14 @@ void swapchain_base::page_flip_thread() sem_post(&m_start_present_semaphore); - present_image(pending_index); + present_image(*pending_index); m_first_present = false; } /* The swapchain has already started presenting. */ else { - present_image(pending_index); + present_image(*pending_index); } } } @@ -139,7 +139,7 @@ swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAll , m_page_flip_thread_run(true) , m_thread_sem_defined(false) , m_first_present(true) - , m_pending_buffer_pool{ nullptr, 0, 0, 0 } + , m_pending_buffer_pool{} , m_allocator(dev_data.get_allocator(), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT, callbacks) , m_swapchain_images(m_allocator) , m_surface(VK_NULL_HANDLE) @@ -185,17 +185,6 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount)) return VK_ERROR_OUT_OF_HOST_MEMORY; - /* Initialize ring buffer. */ - m_pending_buffer_pool.ring = m_allocator.create(m_swapchain_images.size(), 0); - if (m_pending_buffer_pool.ring == nullptr) - { - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - m_pending_buffer_pool.head = 0; - m_pending_buffer_pool.tail = 0; - m_pending_buffer_pool.size = m_swapchain_images.size(); - /* We have allocated images, we can call the platform init function if something needs to be done. */ result = init_platform(device, swapchain_create_info); if (result != VK_SUCCESS) @@ -364,8 +353,6 @@ void swapchain_base::teardown() /* Call implementation specific release */ destroy_image(img); } - - m_allocator.destroy(m_swapchain_images.size(), m_pending_buffer_pool.ring); } VkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index) @@ -457,7 +444,7 @@ VkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count, V } } -VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, const uint32_t image_index) +VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info, uint32_t image_index) { VkResult result; bool descendent_started_presenting = false; @@ -512,19 +499,13 @@ VkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *pr if (descendent_started_presenting) { m_swapchain_images[image_index].status = swapchain_image::FREE; - - m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index; - m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size; + m_pending_buffer_pool.push_back(image_index); m_page_flip_semaphore.post(); - return VK_ERROR_OUT_OF_DATE_KHR; } m_swapchain_images[image_index].status = swapchain_image::PENDING; - - m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index; - m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size; - + m_pending_buffer_pool.push_back(image_index); m_page_flip_semaphore.post(); return VK_SUCCESS; } diff --git a/wsi/swapchain_base.hpp b/wsi/swapchain_base.hpp index c800e5a..8eed897 100644 --- a/wsi/swapchain_base.hpp +++ b/wsi/swapchain_base.hpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include "surface_properties.hpp" namespace wsi { @@ -164,17 +166,6 @@ protected: */ bool m_is_valid; - struct ring_buffer - { - /* Ring buffer to hold the image indexes. */ - uint32_t *ring; - /* Head of the ring. */ - uint32_t head; - /* End of the ring. */ - uint32_t tail; - /* Size of the ring. */ - uint32_t size; - }; /** * @brief A semaphore to be signalled once a page flip event occurs. */ @@ -215,7 +206,7 @@ protected: * threads and we do not allow the application to acquire more images * than we have we eliminate race conditions. */ - ring_buffer m_pending_buffer_pool; + util::ring_buffer m_pending_buffer_pool; /** * @brief User provided memory allocation callbacks. diff --git a/wsi/wayland/surface_properties.cpp b/wsi/wayland/surface_properties.cpp index 7622c4a..50caf83 100644 --- a/wsi/wayland/surface_properties.cpp +++ b/wsi/wayland/surface_properties.cpp @@ -70,8 +70,7 @@ VkResult surface_properties::get_surface_capabilities(VkPhysicalDevice physical_ { /* Image count limits */ pSurfaceCapabilities->minImageCount = 2; - /* There is no maximum theoretically speaking */ - pSurfaceCapabilities->maxImageCount = UINT32_MAX; + pSurfaceCapabilities->maxImageCount = MAX_SWAPCHAIN_IMAGE_COUNT; /* Surface extents */ pSurfaceCapabilities->currentExtent = { 0xffffffff, 0xffffffff };