Use util::allocator in swapchain object
authorMatteo Franchin <matteo.franchin@arm.com>
Fri, 27 Nov 2020 09:59:18 +0000 (09:59 +0000)
committerMatteo Franchin <matteo.franchin@arm.com>
Thu, 10 Dec 2020 17:46:36 +0000 (17:46 +0000)
Change util::allocator's create and destroy methods so that they can
allocate multiple objects, in line with the allocate and deallocate
methods of std::allocator.

Also add documentation for util::allocator and its methods.

Finally, use util::allocator in swapchain.cpp, rather than allocating
memory directly via VkAllocationCallbacks.

Change-Id: I0bc25abe3cbc3af9608218411da8d70e04dd9749
Signed-off-by: Matteo Franchin <matteo.franchin@arm.com>
util/custom_allocator.cpp
util/custom_allocator.hpp
wsi/headless/swapchain.cpp
wsi/swapchain_base.cpp
wsi/swapchain_base.hpp

index 3706401..27d7f09 100644 (file)
@@ -62,4 +62,9 @@ allocator::allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationS
    }
 }
 
+const VkAllocationCallbacks *allocator::get_original_callbacks() const
+{
+   return m_callbacks.pfnAllocation == default_allocation ? nullptr : &m_callbacks;
+}
+
 } /* namespace util */
index cbf880a..8f355e8 100644 (file)
@@ -25,6 +25,7 @@
 #include <new>
 #include <vector>
 #include <string>
+#include <cassert>
 
 #include <vulkan/vulkan.h>
 
@@ -39,13 +40,37 @@ namespace util
 class allocator
 {
 public:
+   /**
+    * @brief Construct a new wrapper for the given VK callbacks and scope.
+    * @param callbacks Pointer to allocation callbacks. If this is @c nullptr, then default
+    *   allocation callbacks are used. These can be accessed through #m_callbacks.
+    * @param scope The scope to use for this allocator.
+    */
    allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationScope scope);
 
+   /**
+    * @brief Get a pointer to the allocation callbacks provided while constructing this object.
+    * @return a copy of the #VkAllocationCallback argument provided in the allocator constructor
+    *   or @c nullptr if this argument was provided as @c nullptr.
+    * @note The #m_callbacks member is always populated with callable pointers for pfnAllocation,
+    *   pfnReallocation and pfnFree.
+    */
+   const VkAllocationCallbacks *get_original_callbacks() const;
+
+   /**
+    * @brief Helper method to allocate and construct objects with a custom allocator.
+    * @param num_objects Number of objects to create.
+    * @return Pointer to the newly created objects or @c nullptr if allocation failed.
+    */
    template <typename T, typename... arg_types>
-   T *create(arg_types &&... args) const;
+   T *create(size_t num_objects, arg_types &&... args) const noexcept;
 
+   /**
+    * @brief Helper method to destroy and deallocate objects constructed with allocator::create().
+    * @param num_objects Number of objects to destroy.
+    */
    template <typename T>
-   void destroy(T *obj) const;
+   void destroy(size_t num_objects, T *obj) const noexcept;
 
    VkAllocationCallbacks m_callbacks;
    VkSystemAllocationScope m_scope;
@@ -118,48 +143,66 @@ bool operator!=(const custom_allocator<T> &, const custom_allocator<U> &)
    return false;
 }
 
-/**
- * @brief Helper method to allocate and construct objects with a custom allocator.
- * @return The new object or @c nullptr if allocation failed.
- */
 template <typename T, typename... arg_types>
-T *allocator::create(arg_types &&... args) const
+T *allocator::create(size_t num_objects, arg_types &&... args) const noexcept
 {
+   if (num_objects < 1)
+   {
+      return nullptr;
+   }
+
    custom_allocator<T> allocator(*this);
    T *ptr;
    try
    {
-      ptr = allocator.allocate(1);
+      ptr = allocator.allocate(num_objects);
    }
    catch (...)
    {
       return nullptr;
    }
 
+   size_t objects_constructed = 0;
    try
    {
-      new (ptr) T(std::forward<arg_types>(args)...);
+      while (objects_constructed < num_objects)
+      {
+         T *next_object = &ptr[objects_constructed];
+         new (next_object) T(std::forward<arg_types>(args)...);
+         objects_constructed++;
+      }
    }
    catch (...)
    {
       /* We catch all exceptions thrown while constructing the object, not just
        * std::bad_alloc.
        */
-      allocator.deallocate(ptr, 1);
+      while (objects_constructed > 0)
+      {
+         objects_constructed--;
+         ptr[objects_constructed].~T();
+      }
+      allocator.deallocate(ptr, num_objects);
       return nullptr;
    }
    return ptr;
 }
 
-/**
- * @brief Helper method to destroy and deallocate objects constructed with create_custom().
- */
 template <typename T>
-void allocator::destroy(T *obj) const
+void allocator::destroy(size_t num_objects, T *objects) const noexcept
 {
-   obj->~T();
+   assert((objects == nullptr) == (num_objects == 0));
+   if (num_objects == 0)
+   {
+      return;
+   }
+
    custom_allocator<T> allocator(*this);
-   allocator.deallocate(obj, 1);
+   for (size_t i = 0; i < num_objects; i++)
+   {
+      objects[i].~T();
+   }
+   allocator.deallocate(objects, num_objects);
 }
 
 template <typename T>
index a12f29b..03af79b 100644 (file)
@@ -88,19 +88,10 @@ VkResult swapchain::create_image(const VkImageCreateInfo &image_create, wsi::swa
    image_data *data = nullptr;
 
    /* Create image_data */
-   if (m_alloc_callbacks != nullptr)
-   {
-      data = static_cast<image_data *>(m_alloc_callbacks->pfnAllocation(
-         m_alloc_callbacks->pUserData, sizeof(image_data), 0, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
-   }
-   else
-   {
-      data = static_cast<image_data *>(malloc(sizeof(image_data)));
-   }
-
+   data = m_allocator.create<image_data>(1);
    if (data == nullptr)
    {
-      m_device_data.disp.DestroyImage(m_device, image.image, m_alloc_callbacks);
+      m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
       return VK_ERROR_OUT_OF_HOST_MEMORY;
    }
    image.data = reinterpret_cast<void *>(data);
@@ -150,7 +141,7 @@ void swapchain::destroy_image(wsi::swapchain_image &image)
 
       if (image.image != VK_NULL_HANDLE)
       {
-         m_device_data.disp.DestroyImage(m_device, image.image, m_alloc_callbacks);
+         m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());
          image.image = VK_NULL_HANDLE;
       }
    }
@@ -163,14 +154,7 @@ void swapchain::destroy_image(wsi::swapchain_image &image)
          m_device_data.disp.FreeMemory(m_device, data->memory, nullptr);
          data->memory = VK_NULL_HANDLE;
       }
-      if (m_alloc_callbacks != nullptr)
-      {
-         m_alloc_callbacks->pfnFree(m_alloc_callbacks->pUserData, data);
-      }
-      else
-      {
-         free(data);
-      }
+      m_allocator.destroy(1, data);
       image.data = nullptr;
    }
 
index be50e36..098f51e 100644 (file)
@@ -134,14 +134,14 @@ void swapchain_base::unpresent_image(uint32_t presented_index)
    m_free_image_semaphore.post();
 }
 
-swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator)
+swapchain_base::swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *callbacks)
    : m_device_data(dev_data)
    , m_page_flip_thread_run(true)
    , m_thread_sem_defined(false)
    , m_first_present(true)
    , m_pending_buffer_pool{ nullptr, 0, 0, 0 }
-   , m_alloc_callbacks(allocator)
-   , m_swapchain_images(util::allocator(m_alloc_callbacks, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT))
+   , m_allocator(callbacks, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)
+   , m_swapchain_images(m_allocator)
    , m_surface(VK_NULL_HANDLE)
    , m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR)
    , m_descendant(VK_NULL_HANDLE)
@@ -184,17 +184,7 @@ VkResult swapchain_base::init(VkDevice device, const VkSwapchainCreateInfoKHR *s
       return VK_ERROR_OUT_OF_HOST_MEMORY;
 
    /* Initialize ring buffer. */
-   if (m_alloc_callbacks != nullptr)
-   {
-      m_pending_buffer_pool.ring = static_cast<uint32_t *>(
-         m_alloc_callbacks->pfnAllocation(m_alloc_callbacks->pUserData, sizeof(uint32_t) * m_swapchain_images.size(),
-                                          alignof(uint32_t), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
-   }
-   else
-   {
-      m_pending_buffer_pool.ring = static_cast<uint32_t *>(malloc(sizeof(uint32_t) * m_swapchain_images.size()));
-   }
-
+   m_pending_buffer_pool.ring = m_allocator.create<uint32_t>(m_swapchain_images.size(), 0);
    if (m_pending_buffer_pool.ring == nullptr)
    {
       return VK_ERROR_OUT_OF_HOST_MEMORY;
@@ -377,17 +367,7 @@ void swapchain_base::teardown()
       destroy_image(img);
    }
 
-   if (m_pending_buffer_pool.ring != nullptr)
-   {
-      if (m_alloc_callbacks != nullptr)
-      {
-         m_alloc_callbacks->pfnFree(m_alloc_callbacks->pUserData, m_pending_buffer_pool.ring);
-      }
-      else
-      {
-         free(m_pending_buffer_pool.ring);
-      }
-   }
+   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)
index 202ccf3..ccd5270 100644 (file)
@@ -200,7 +200,7 @@ protected:
    /**
     * @brief User provided memory allocation callbacks.
     */
-   const VkAllocationCallbacks *m_alloc_callbacks;
+   const util::allocator m_allocator;
 
    /**
     * @brief Vector of images in the swapchain.
@@ -243,7 +243,15 @@ protected:
     */
    VkQueue m_queue;
 
-   /*
+   /**
+    * @brief Return the VkAllocationCallbacks passed in this object constructor.
+    */
+   const VkAllocationCallbacks *get_allocation_callbacks()
+   {
+      return m_allocator.get_original_callbacks();
+   }
+
+   /**
     * @brief Method to wait on all pending buffers to be displayed.
     */
    void wait_for_pending_buffers();