Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/drape
diff options
context:
space:
mode:
authorr.kuznetsov <r.kuznetsov@corp.mail.ru>2019-01-23 12:48:24 +0300
committerDaria Volvenkova <d.volvenkova@corp.mail.ru>2019-03-01 10:45:24 +0300
commitcc4222a69fdb0776abba626a3389018084b549f0 (patch)
tree12332d25d15ca58b48555ff2e57f8ba3aed7c19a /drape
parentf53013310a9ce31e976e7bae78461e57b12cc0d2 (diff)
[vulkan] Implemented memory and objects managers
Diffstat (limited to 'drape')
-rw-r--r--drape/vulkan/vulkan_memory_manager.cpp275
-rw-r--r--drape/vulkan/vulkan_memory_manager.hpp82
-rw-r--r--drape/vulkan/vulkan_object_manager.cpp128
-rw-r--r--drape/vulkan/vulkan_object_manager.hpp48
4 files changed, 531 insertions, 2 deletions
diff --git a/drape/vulkan/vulkan_memory_manager.cpp b/drape/vulkan/vulkan_memory_manager.cpp
index 896c6c6591..1b4c3cfcba 100644
--- a/drape/vulkan/vulkan_memory_manager.cpp
+++ b/drape/vulkan/vulkan_memory_manager.cpp
@@ -1,9 +1,284 @@
#include "drape/vulkan/vulkan_memory_manager.hpp"
+#include "drape/vulkan/vulkan_utils.hpp"
+
+#include "base/assert.hpp"
+
+#include <algorithm>
+#include <limits>
namespace dp
{
namespace vulkan
{
+namespace
+{
+std::array<uint32_t, VulkanMemoryManager::kResourcesCount> const kMinBlockSizeInBytes =
+{
+ 64 * 1024, // Geometry
+ 4 * 1024, // Uniform
+ 0, // Staging
+ 0, // Image
+};
+
+std::array<uint32_t, VulkanMemoryManager::kResourcesCount> const kDesiredSizeInBytes =
+{
+ 50 * 1024 * 1024, // Geometry
+ std::numeric_limits<uint32_t>::max(), // Uniform (unlimited)
+ 10 * 1024 * 1024, // Staging
+ std::numeric_limits<uint32_t>::max(), // Image (unlimited)
+};
+
+VkMemoryPropertyFlags GetMemoryPropertyFlags(VulkanMemoryManager::ResourceType resourceType,
+ boost::optional<VkMemoryPropertyFlags> & fallbackTypeBits)
+{
+ switch (resourceType)
+ {
+ case VulkanMemoryManager::ResourceType::Geometry:
+ case VulkanMemoryManager::ResourceType::Staging:
+ fallbackTypeBits = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+ case VulkanMemoryManager::ResourceType::Uniform:
+ // No fallback.
+ return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+
+ case VulkanMemoryManager::ResourceType::Image:
+ // No fallback.
+ return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+
+ case VulkanMemoryManager::ResourceType::Count:
+ CHECK(false, ());
+ }
+ return 0;
+}
+
+uint32_t GetAligned(uint32_t value, uint32_t alignment)
+{
+ if (alignment == 0)
+ return value;
+ return (value + alignment - 1) & ~(alignment - 1);
+}
+} // namespace
+
+VulkanMemoryManager::~VulkanMemoryManager()
+{
+ for (size_t i = 0; i < kResourcesCount; ++i)
+ {
+ for (auto const & b : m_freeBlocks[i])
+ vkFreeMemory(m_device, b.m_memory, nullptr);
+
+ for (auto const & p : m_memory[i])
+ {
+ for (auto const & b : p.second)
+ vkFreeMemory(m_device, b.m_memory, nullptr);
+ }
+ }
+}
+
+boost::optional<uint32_t> VulkanMemoryManager::GetMemoryTypeIndex(uint32_t typeBits,
+ VkMemoryPropertyFlags properties) const
+{
+ for (uint32_t i = 0; i < m_memoryProperties.memoryTypeCount; i++)
+ {
+ if ((typeBits & 1) == 1)
+ {
+ if ((m_memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
+ return i;
+ }
+ typeBits >>= 1;
+ }
+ return {};
+}
+
+uint32_t VulkanMemoryManager::GetOffsetAlignment(ResourceType resourceType) const
+{
+ if (resourceType == ResourceType::Uniform)
+ return static_cast<uint32_t>(m_deviceLimits.minUniformBufferOffsetAlignment);
+
+ return static_cast<uint32_t>(m_deviceLimits.minMemoryMapAlignment);
+}
+
+VulkanMemoryManager::AllocationPtr VulkanMemoryManager::Allocate(ResourceType resourceType,
+ VkMemoryRequirements memReqs,
+ uint64_t blockHash)
+{
+ auto const alignedSize = GetAligned(static_cast<uint32_t>(memReqs.size),
+ static_cast<uint32_t>(memReqs.alignment));
+ // Looking for an existed block.
+ {
+ auto & m = m_memory[static_cast<size_t>(resourceType)];
+ auto const it = m.find(blockHash);
+ if (it != m.end())
+ {
+ CHECK(!it->second.empty(), ());
+ auto & block = it->second.back();
+ auto const alignedOffset = GetAligned(block.m_freeOffset, GetOffsetAlignment(resourceType));
+
+ // There is space in the current block.
+ if (block.m_blockSize <= alignedOffset + alignedSize)
+ {
+ block.m_freeOffset = alignedOffset + alignedSize;
+ block.m_allocationCounter++;
+ return std::make_shared<Allocation>(resourceType, blockHash, block.m_memory,
+ alignedOffset, alignedSize, block.m_isCoherent);
+ }
+ }
+
+ // Looking for a block in free ones.
+ auto & fm = m_freeBlocks[static_cast<size_t>(resourceType)];
+ // Free blocks array must be sorted by size.
+ MemoryBlock refBlock;
+ refBlock.m_blockSize = alignedSize;
+ auto const freeBlockIt = std::upper_bound(fm.begin(), fm.end(), refBlock);
+ if (freeBlockIt != fm.end())
+ {
+ MemoryBlock freeBlock = *freeBlockIt;
+ CHECK_EQUAL(freeBlock.m_allocationCounter, 0, ());
+ CHECK_EQUAL(freeBlock.m_freeOffset, 0, ());
+ CHECK_LESS_OR_EQUAL(alignedSize, freeBlock.m_blockSize, ());
+ fm.erase(freeBlockIt);
+
+ freeBlock.m_freeOffset = alignedSize;
+ freeBlock.m_allocationCounter++;
+ auto p = std::make_shared<Allocation>(resourceType, blockHash, freeBlock.m_memory,
+ 0, alignedSize, freeBlock.m_isCoherent);
+ m[blockHash].push_back(std::move(freeBlock));
+ return p;
+ }
+ }
+
+ // Looking for memory index by memory properties.
+ boost::optional<VkMemoryPropertyFlags> fallbackFlags;
+ auto flags = GetMemoryPropertyFlags(resourceType, fallbackFlags);
+ auto memoryTypeIndex = GetMemoryTypeIndex(memReqs.memoryTypeBits, flags);
+ if (!memoryTypeIndex && fallbackFlags)
+ {
+ flags = fallbackFlags.value();
+ memoryTypeIndex = GetMemoryTypeIndex(memReqs.memoryTypeBits, flags);
+ if (!memoryTypeIndex)
+ CHECK(false, ("Unsupported memory allocation configuration."));
+ }
+ else
+ {
+ CHECK(false, ("Unsupported memory allocation configuration."));
+ }
+
+ // Create new memory block.
+ auto const blockSize = std::max(kMinBlockSizeInBytes[static_cast<size_t>(resourceType)],
+ alignedSize);
+ VkDeviceMemory memory = {};
+ VkMemoryAllocateInfo memAllocInfo = {};
+ memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memAllocInfo.pNext = nullptr;
+ memAllocInfo.allocationSize = blockSize;
+ memAllocInfo.memoryTypeIndex = memoryTypeIndex.value();
+ CHECK_VK_CALL(vkAllocateMemory(m_device, &memAllocInfo, nullptr, &memory));
+ m_sizes[static_cast<size_t>(resourceType)] += blockSize;
+
+ // Attach block.
+ auto & m = m_memory[static_cast<size_t>(resourceType)];
+
+ MemoryBlock newBlock;
+ newBlock.m_memory = memory;
+ newBlock.m_blockSize = blockSize;
+ newBlock.m_freeOffset = alignedSize;
+ newBlock.m_allocationCounter++;
+ newBlock.m_isCoherent = ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0);
+
+ auto p = std::make_shared<Allocation>(resourceType, blockHash, newBlock.m_memory,
+ 0, alignedSize, newBlock.m_isCoherent);
+ m[blockHash].push_back(std::move(newBlock));
+ return p;
+}
+
+void VulkanMemoryManager::BeginDeallocationSession()
+{
+ m_isInDeallocationSession = true;
+ m_deallocationSessionMask = 0;
+}
+
+void VulkanMemoryManager::Deallocate(AllocationPtr ptr)
+{
+ CHECK(ptr, ());
+ auto const kResourceIndex = static_cast<size_t>(ptr->m_resourceType);
+ auto & m = m_memory[kResourceIndex];
+ auto const it = m.find(ptr->m_blockHash);
+ CHECK(it != m.end(), ());
+ auto blockIt = std::find_if(it->second.begin(), it->second.end(),
+ [&ptr](MemoryBlock const & b)
+ {
+ return b.m_memory == ptr->m_memory;
+ });
+ CHECK(blockIt != it->second.end(), ());
+ CHECK_GREATER(blockIt->m_allocationCounter, 0, ());
+ blockIt->m_allocationCounter--;
+
+ if (blockIt->m_allocationCounter == 0)
+ {
+ if (m_isInDeallocationSession)
+ {
+ // Here we set a bit in the deallocation mask to skip the processing of untouched
+ // resource collections.
+ m_deallocationSessionMask |= (1 << kResourceIndex);
+ }
+ else
+ {
+ MemoryBlock memoryBlock = *blockIt;
+ it->second.erase(blockIt);
+ if (m_sizes[kResourceIndex] > kDesiredSizeInBytes[kResourceIndex])
+ {
+ CHECK_LESS_OR_EQUAL(memoryBlock.m_blockSize, m_sizes[kResourceIndex], ());
+ m_sizes[kResourceIndex] -= memoryBlock.m_blockSize;
+ vkFreeMemory(m_device, memoryBlock.m_memory, nullptr);
+ }
+ else
+ {
+ memoryBlock.m_freeOffset = 0;
+ auto & fm = m_freeBlocks[kResourceIndex];
+ fm.push_back(std::move(memoryBlock));
+ std::sort(fm.begin(), fm.end());
+ }
+ }
+ }
+}
+
+void VulkanMemoryManager::EndDeallocationSession()
+{
+ if (!m_isInDeallocationSession)
+ return;
+
+ m_isInDeallocationSession = false;
+
+ for (size_t i = 0; i < kResourcesCount; ++i)
+ {
+ if (((m_deallocationSessionMask >> i) & 1) == 0)
+ continue;
+ auto & fm = m_freeBlocks[i];
+ auto & m = m_memory[i];
+ m[i].erase(std::remove_if(m[i].begin(), m[i].end(),
+ [this, &fm, i](MemoryBlock const & b)
+ {
+ if (b.m_allocationCounter == 0)
+ {
+ if (m_sizes[i] > kDesiredSizeInBytes[i])
+ {
+ CHECK_LESS_OR_EQUAL(b.m_blockSize, m_sizes[i], ());
+ m_sizes[i] -= b.m_blockSize;
+ vkFreeMemory(m_device, b.m_memory, nullptr);
+ }
+ else
+ {
+ MemoryBlock block = b;
+ block.m_freeOffset = 0;
+ fm.push_back(std::move(block));
+ }
+ return true;
+ }
+ return false;
+ }), m[i].end());
+ std::sort(fm.begin(), fm.end());
+ }
+}
} // namespace vulkan
} // namespace dp
diff --git a/drape/vulkan/vulkan_memory_manager.hpp b/drape/vulkan/vulkan_memory_manager.hpp
index 35f6031d2f..18449c0838 100644
--- a/drape/vulkan/vulkan_memory_manager.hpp
+++ b/drape/vulkan/vulkan_memory_manager.hpp
@@ -3,19 +3,97 @@
#include <vulkan_wrapper.h>
#include <vulkan/vulkan.h>
+#include <boost/optional.hpp>
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <unordered_map>
+
namespace dp
{
namespace vulkan
{
+// NOTE: The class is not thread safe and must be externally synchronized.
class VulkanMemoryManager
{
public:
- explicit VulkanMemoryManager(VkDevice device) : m_device(device) {}
+ VulkanMemoryManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits,
+ VkPhysicalDeviceMemoryProperties const & memoryProperties)
+ : m_device(device)
+ , m_deviceLimits(deviceLimits)
+ , m_memoryProperties(memoryProperties)
+ {}
+
+ ~VulkanMemoryManager();
+
+ enum class ResourceType : uint8_t
+ {
+ Geometry = 0,
+ Uniform,
+ Staging,
+ Image,
+
+ Count
+ };
+ static size_t constexpr kResourcesCount =
+ static_cast<uint32_t>(VulkanMemoryManager::ResourceType::Count);
+
+ struct Allocation
+ {
+ uint64_t const m_blockHash;
+ VkDeviceMemory const m_memory;
+ uint32_t const m_offset;
+ uint32_t const m_size;
+ ResourceType const m_resourceType;
+ bool const m_isCoherent;
- //VkDeviceMemory
+ Allocation(ResourceType resourceType, uint64_t blockHash, VkDeviceMemory memory,
+ uint32_t offset, uint32_t size, bool isCoherent)
+ : m_blockHash(blockHash)
+ , m_memory(memory)
+ , m_offset(offset)
+ , m_size(size)
+ , m_resourceType(resourceType)
+ , m_isCoherent(isCoherent)
+ {}
+ };
+
+ using AllocationPtr = std::shared_ptr<Allocation>;
+
+ AllocationPtr Allocate(ResourceType resourceType, VkMemoryRequirements memReqs,
+ uint64_t blockHash);
+ void BeginDeallocationSession();
+ void Deallocate(AllocationPtr ptr);
+ void EndDeallocationSession();
private:
+ boost::optional<uint32_t> GetMemoryTypeIndex(uint32_t typeBits,
+ VkMemoryPropertyFlags properties) const;
+ uint32_t GetOffsetAlignment(ResourceType resourceType) const;
+
VkDevice const m_device;
+ VkPhysicalDeviceLimits const m_deviceLimits;
+ VkPhysicalDeviceMemoryProperties const m_memoryProperties;
+ bool m_isInDeallocationSession = false;
+ uint32_t m_deallocationSessionMask = 0;
+
+ struct MemoryBlock
+ {
+ VkDeviceMemory m_memory = {};
+ uint32_t m_blockSize = 0;
+ uint32_t m_freeOffset = 0;
+ uint32_t m_allocationCounter = 0;
+ bool m_isCoherent = false;
+
+ bool operator<(MemoryBlock const & b) const { return m_blockSize < b.m_blockSize; }
+ };
+
+ std::array<std::unordered_map<uint64_t, std::vector<MemoryBlock>>, kResourcesCount> m_memory;
+ std::array<std::vector<MemoryBlock>, kResourcesCount> m_freeBlocks;
+ std::array<uint32_t, kResourcesCount> m_sizes = {};
};
} // namespace vulkan
} // namespace dp
diff --git a/drape/vulkan/vulkan_object_manager.cpp b/drape/vulkan/vulkan_object_manager.cpp
new file mode 100644
index 0000000000..c7e86e4312
--- /dev/null
+++ b/drape/vulkan/vulkan_object_manager.cpp
@@ -0,0 +1,128 @@
+#include "drape/vulkan/vulkan_object_manager.hpp"
+#include "drape/vulkan/vulkan_utils.hpp"
+
+namespace dp
+{
+namespace vulkan
+{
+VulkanObjectManager::VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits,
+ VkPhysicalDeviceMemoryProperties const & memoryProperties,
+ uint32_t queueFamilyIndex)
+ : m_device(device)
+ , m_queueFamilyIndex(queueFamilyIndex)
+ , m_memoryManager(device, deviceLimits, memoryProperties)
+{
+ m_queueToDestroy.reserve(50);
+}
+
+VulkanObjectManager::~VulkanObjectManager()
+{
+ CollectObjects();
+}
+
+VulkanObject VulkanObjectManager::CreateBuffer(VulkanMemoryManager::ResourceType resourceType,
+ uint32_t sizeInBytes, uint64_t batcherHash)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ VulkanObject result;
+ VkBufferCreateInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ info.pNext = nullptr;
+ info.flags = 0;
+ info.size = sizeInBytes;
+ if (resourceType == VulkanMemoryManager::ResourceType::Geometry)
+ info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ else if (resourceType == VulkanMemoryManager::ResourceType::Uniform)
+ info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ else if (resourceType == VulkanMemoryManager::ResourceType::Staging)
+ info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ else
+ CHECK(false, ("Unsupported resource type."));
+
+ info.usage = VK_SHARING_MODE_EXCLUSIVE;
+ info.queueFamilyIndexCount = 1;
+ info.pQueueFamilyIndices = &m_queueFamilyIndex;
+ CHECK_VK_CALL(vkCreateBuffer(m_device, &info, nullptr, &result.m_buffer));
+
+ VkMemoryRequirements memReqs = {};
+ vkGetBufferMemoryRequirements(m_device, result.m_buffer, &memReqs);
+
+ result.m_allocation = m_memoryManager.Allocate(resourceType, memReqs, batcherHash);
+ return result;
+}
+
+VulkanObject VulkanObjectManager::CreateImage(VkImageUsageFlagBits usageFlagBits, VkFormat format,
+ VkImageAspectFlags aspectFlags, uint32_t width, uint32_t height)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ VulkanObject result;
+ VkImageCreateInfo imageCreateInfo = {};
+ imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageCreateInfo.pNext = nullptr;
+ imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageCreateInfo.format = format;
+ imageCreateInfo.mipLevels = 1;
+ imageCreateInfo.arrayLayers = 1;
+ imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageCreateInfo.extent = { width, height, 1 };
+ imageCreateInfo.usage = usageFlagBits | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ CHECK_VK_CALL(vkCreateImage(m_device, &imageCreateInfo, nullptr, &result.m_image));
+
+ VkImageViewCreateInfo viewCreateInfo = {};
+ viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewCreateInfo.pNext = nullptr;
+ viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewCreateInfo.format = format;
+ viewCreateInfo.components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
+ VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
+ viewCreateInfo.subresourceRange.aspectMask = aspectFlags;
+ viewCreateInfo.subresourceRange.baseMipLevel = 0;
+ viewCreateInfo.subresourceRange.levelCount = 1;
+ viewCreateInfo.subresourceRange.baseArrayLayer = 0;
+ viewCreateInfo.subresourceRange.layerCount = 1;
+ viewCreateInfo.image = result.m_image;
+ CHECK_VK_CALL(vkCreateImageView(m_device, &viewCreateInfo, nullptr, &result.m_imageView));
+
+ VkMemoryRequirements memReqs = {};
+ vkGetImageMemoryRequirements(m_device, result.m_image, &memReqs);
+
+ result.m_allocation = m_memoryManager.Allocate(VulkanMemoryManager::ResourceType::Image,
+ memReqs, 0 /* blockHash */);
+ return result;
+}
+
+void VulkanObjectManager::DestroyObject(VulkanObject object)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_queueToDestroy.push_back(std::move(object));
+}
+
+void VulkanObjectManager::CollectObjects()
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (m_queueToDestroy.empty())
+ return;
+
+ m_memoryManager.BeginDeallocationSession();
+ for (size_t i = 0; i < m_queueToDestroy.size(); ++i)
+ {
+ if (m_queueToDestroy[i].m_buffer != 0)
+ vkDestroyBuffer(m_device, m_queueToDestroy[i].m_buffer, nullptr);
+ if (m_queueToDestroy[i].m_imageView != 0)
+ vkDestroyImageView(m_device, m_queueToDestroy[i].m_imageView, nullptr);
+ if (m_queueToDestroy[i].m_image != 0)
+ vkDestroyImage(m_device, m_queueToDestroy[i].m_image, nullptr);
+
+ if (m_queueToDestroy[i].m_allocation)
+ m_memoryManager.Deallocate(m_queueToDestroy[i].m_allocation);
+ }
+ m_memoryManager.EndDeallocationSession();
+ m_queueToDestroy.clear();
+}
+} // namespace vulkan
+} // namespace dp
diff --git a/drape/vulkan/vulkan_object_manager.hpp b/drape/vulkan/vulkan_object_manager.hpp
new file mode 100644
index 0000000000..560faffd93
--- /dev/null
+++ b/drape/vulkan/vulkan_object_manager.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "drape/vulkan/vulkan_memory_manager.hpp"
+
+#include <vulkan_wrapper.h>
+#include <vulkan/vulkan.h>
+
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace dp
+{
+namespace vulkan
+{
+struct VulkanObject
+{
+ VkBuffer m_buffer = {};
+ VkImage m_image = {};
+ VkImageView m_imageView = {};
+ VulkanMemoryManager::AllocationPtr m_allocation;
+};
+
+class VulkanObjectManager
+{
+public:
+ VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits const & deviceLimits,
+ VkPhysicalDeviceMemoryProperties const & memoryProperties,
+ uint32_t queueFamilyIndex);
+ ~VulkanObjectManager();
+
+ VulkanObject CreateBuffer(VulkanMemoryManager::ResourceType resourceType,
+ uint32_t sizeInBytes, uint64_t batcherHash);
+ VulkanObject CreateImage(VkImageUsageFlagBits usageFlagBits, VkFormat format,
+ VkImageAspectFlags aspectFlags, uint32_t width, uint32_t height);
+ void DestroyObject(VulkanObject object);
+ void CollectObjects();
+
+private:
+ VkDevice const m_device;
+ uint32_t const m_queueFamilyIndex;
+ VulkanMemoryManager m_memoryManager;
+ std::vector<VulkanObject> m_queueToDestroy;
+ std::mutex m_mutex;
+};
+} // namespace vulkan
+} // namespace dp