From 292eb1de1b954cf8d802435ac264ef5e1ae7fc96 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Fri, 9 Jul 2021 20:19:51 +0200
Subject: [PATCH] [#59] Implemented VMA into buffer and image managers

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 include/vkcv/BufferManager.hpp |   4 +-
 src/vkcv/BufferManager.cpp     | 129 ++++++++++++---------------------
 src/vkcv/ImageManager.cpp      |  92 ++++++++++++-----------
 src/vkcv/ImageManager.hpp      |  37 ++++------
 4 files changed, 108 insertions(+), 154 deletions(-)

diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index 9eb80d70..c7f32d9f 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -2,6 +2,7 @@
 
 #include <vector>
 #include <vulkan/vulkan.hpp>
+#include <vk_mem_alloc.hpp>
 
 #include "Handles.hpp"
 
@@ -30,9 +31,8 @@ namespace vkcv
 		struct Buffer
 		{
 			vk::Buffer m_handle;
-			vk::DeviceMemory m_memory;
+			vma::Allocation m_allocation;
 			size_t m_size = 0;
-			void* m_mapped = nullptr;
 			bool m_mappable = false;
 		};
 		
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index aec96411..cfa23329 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -28,32 +28,6 @@ namespace vkcv {
 		}
 	}
 	
-	/**
-	 * @brief searches memory type index for buffer allocation, combines requirements of buffer and application
-	 * @param physicalMemoryProperties Memory Properties of physical device
-	 * @param typeBits Bit field for suitable memory types
-	 * @param requirements Property flags that are required
-	 * @return memory type index for Buffer
-	 */
-	uint32_t searchBufferMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) {
-		const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount;
-		for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) {
-			const uint32_t memoryTypeBits = (1 << memoryIndex);
-			const bool isRequiredMemoryType = typeBits & memoryTypeBits;
-
-			const vk::MemoryPropertyFlags properties =
-				physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags;
-			const bool hasRequiredProperties =
-				(properties & requirements) == requirements;
-
-			if (isRequiredMemoryType && hasRequiredProperties)
-				return static_cast<int32_t>(memoryIndex);
-		}
-
-		// failed to find memory type
-		return -1;
-	}
-	
 	BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
@@ -83,43 +57,48 @@ namespace vkcv {
 			usageFlags |= vk::BufferUsageFlagBits::eTransferDst;
 		}
 		
-		const vk::Device& device = m_core->getContext().getDevice();
-		
-		vk::Buffer buffer = device.createBuffer(
-				vk::BufferCreateInfo(createFlags, size, usageFlags)
-		);
-		
-		const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer);
-		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
 		vk::MemoryPropertyFlags memoryTypeFlags;
+		vma::MemoryUsage memoryUsage;
 		bool mappable = false;
 		
 		switch (memoryType) {
 			case BufferMemoryType::DEVICE_LOCAL:
 				memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
+				memoryUsage = vma::MemoryUsage::eGpuOnly;
+				mappable = false;
 				break;
 			case BufferMemoryType::HOST_VISIBLE:
 				memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
+				memoryUsage = vma::MemoryUsage::eCpuOnly;
 				mappable = true;
 				break;
 			default:
-				// TODO: maybe an issue
+				vkcv_log(LogLevel::WARNING, "Unknown buffer memory type");
+				memoryUsage = vma::MemoryUsage::eUnknown;
+				mappable = false;
 				break;
 		}
 		
-		const uint32_t memoryTypeIndex = searchBufferMemoryType(
-				physicalDevice.getMemoryProperties(),
-				requirements.memoryTypeBits,
-				memoryTypeFlags
+		auto bufferAllocation = allocator.createBuffer(
+				vk::BufferCreateInfo(createFlags, size, usageFlags),
+				vma::AllocationCreateInfo(
+						vma::AllocationCreateFlags(),
+						memoryUsage,
+						memoryTypeFlags,
+						memoryTypeFlags,
+						0,
+						vma::Pool(),
+						nullptr
+				)
 		);
 		
-		vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex));
-		
-		device.bindBufferMemory(buffer, memory, 0);
+		vk::Buffer buffer = bufferAllocation.first;
+		vma::Allocation allocation = bufferAllocation.second;
 		
 		const uint64_t id = m_buffers.size();
-		m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
+		m_buffers.push_back({ buffer, allocation, size, mappable });
 		return BufferHandle(id, [&](uint64_t id) { destroyBufferById(id); });
 	}
 	
@@ -130,7 +109,7 @@ namespace vkcv {
 		
 		vk::Buffer buffer;
 		vk::Buffer stagingBuffer;
-		vk::DeviceMemory stagingMemory;
+		vma::Allocation stagingAllocation;
 		
 		size_t stagingLimit;
 		size_t stagingPosition;
@@ -150,11 +129,11 @@ namespace vkcv {
 		const size_t remaining = info.size - info.stagingPosition;
 		const size_t mapped_size = std::min(remaining, info.stagingLimit);
 		
-		const vk::Device& device = core->getContext().getDevice();
+		const vma::Allocator& allocator = core->getContext().getAllocator();
 		
-		void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size);
+		void* mapped = allocator.mapMemory(info.stagingAllocation);
 		memcpy(mapped, reinterpret_cast<const char*>(info.data) + info.stagingPosition, mapped_size);
-		device.unmapMemory(info.stagingMemory);
+		allocator.unmapMemory(info.stagingAllocation);
 		
 		SubmitInfo submitInfo;
 		submitInfo.queueType = QueueType::Transfer;
@@ -216,7 +195,13 @@ namespace vkcv {
 		
 		auto& buffer = m_buffers[id];
 		
-		return buffer.m_memory;
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		
+		auto info = allocator.getAllocationInfo(
+				buffer.m_allocation
+		);
+		
+		return info.deviceMemory;
 	}
 	
 	void BufferManager::fillBuffer(const BufferHandle& handle, const void *data, size_t size, size_t offset) {
@@ -232,11 +217,7 @@ namespace vkcv {
 		
 		auto& buffer = m_buffers[id];
 		
-		if (buffer.m_mapped) {
-			return;
-		}
-		
-		const vk::Device& device = m_core->getContext().getDevice();
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
 		if (offset > buffer.m_size) {
 			return;
@@ -245,9 +226,9 @@ namespace vkcv {
 		const size_t max_size = std::min(size, buffer.m_size - offset);
 		
 		if (buffer.m_mappable) {
-			void* mapped = device.mapMemory(buffer.m_memory, offset, max_size);
-			memcpy(mapped, data, max_size);
-			device.unmapMemory(buffer.m_memory);
+			void* mapped = allocator.mapMemory(buffer.m_allocation);
+			memcpy(reinterpret_cast<char*>(mapped) + offset, data, max_size);
+			allocator.unmapMemory(buffer.m_allocation);
 		} else {
 			auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ];
 			
@@ -258,11 +239,9 @@ namespace vkcv {
 			
 			info.buffer = buffer.m_handle;
 			info.stagingBuffer = stagingBuffer.m_handle;
-			info.stagingMemory = stagingBuffer.m_memory;
+			info.stagingAllocation = stagingBuffer.m_allocation;
 			
-			const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle);
-			
-			info.stagingLimit = stagingRequirements.size;
+			info.stagingLimit = stagingBuffer.m_size;
 			info.stagingPosition = 0;
 			
 			copyFromStagingBuffer(m_core, info);
@@ -282,19 +261,13 @@ namespace vkcv {
 		
 		auto& buffer = m_buffers[id];
 		
-		if (buffer.m_mapped) {
-			return nullptr;
-		}
-		
-		const vk::Device& device = m_core->getContext().getDevice();
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
 		if (offset > buffer.m_size) {
 			return nullptr;
 		}
 		
-		const size_t max_size = std::min(size, buffer.m_size - offset);
-		buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_size);
-		return buffer.m_mapped;
+		return reinterpret_cast<char*>(allocator.mapMemory(buffer.m_allocation)) + offset;
 	}
 	
 	void BufferManager::unmapBuffer(const BufferHandle& handle) {
@@ -306,14 +279,9 @@ namespace vkcv {
 		
 		auto& buffer = m_buffers[id];
 		
-		if (buffer.m_mapped == nullptr) {
-			return;
-		}
-		
-		const vk::Device& device = m_core->getContext().getDevice();
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
-		device.unmapMemory(buffer.m_memory);
-		buffer.m_mapped = nullptr;
+		allocator.unmapMemory(buffer.m_allocation);
 	}
 	
 	void BufferManager::destroyBufferById(uint64_t id) {
@@ -323,16 +291,13 @@ namespace vkcv {
 		
 		auto& buffer = m_buffers[id];
 		
-		const vk::Device& device = m_core->getContext().getDevice();
-		
-		if (buffer.m_memory) {
-			device.freeMemory(buffer.m_memory);
-			buffer.m_memory = nullptr;
-		}
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 		
 		if (buffer.m_handle) {
-			device.destroyBuffer(buffer.m_handle);
+			allocator.destroyBuffer(buffer.m_handle, buffer.m_allocation);
+			
 			buffer.m_handle = nullptr;
+			buffer.m_allocation = nullptr;
 		}
 	}
 
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index ba96cf8f..1cb6ad3a 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -12,26 +12,6 @@
 
 namespace vkcv {
 
-	ImageManager::Image::Image(
-		vk::Image                   handle,
-		vk::DeviceMemory            memory,
-		std::vector<vk::ImageView>  views,
-		uint32_t                    width,
-		uint32_t                    height,
-		uint32_t                    depth,
-		vk::Format                  format,
-		uint32_t                    layers)
-		:
-		m_handle(handle),
-		m_memory(memory),
-        m_viewPerMip(views),
-		m_width(width),
-		m_height(height),
-		m_depth(depth),
-		m_format(format),
-		m_layers(layers)
-	{}
-
 	/**
 	 * @brief searches memory type index for image allocation, combines requirements of image and application
 	 * @param physicalMemoryProperties Memory Properties of physical device
@@ -124,7 +104,7 @@ namespace vkcv {
 			imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
 		}
 
-		const vk::Device& device = m_core->getContext().getDevice();
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 
 		vk::ImageType imageType = vk::ImageType::e3D;
 		vk::ImageViewType imageViewType = vk::ImageViewType::e3D;
@@ -158,7 +138,7 @@ namespace vkcv {
 
 		vk::SampleCountFlagBits sampleCountFlag = msaaToVkSampleCountFlag(msaa);
 
-		const vk::ImageCreateInfo imageCreateInfo(
+		const vk::ImageCreateInfo imageCreateInfo (
 			createFlags,
 			imageType,
 			format,
@@ -172,21 +152,22 @@ namespace vkcv {
 			{},
 			vk::ImageLayout::eUndefined
 		);
-
-		vk::Image image = device.createImage(imageCreateInfo);
 		
-		const vk::MemoryRequirements requirements = device.getImageMemoryRequirements(image);
-
-		vk::MemoryPropertyFlags memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal;
-
-		const uint32_t memoryTypeIndex = searchImageMemoryType(
-			physicalDevice.getMemoryProperties(),
-			requirements.memoryTypeBits,
-			memoryTypeFlags
+		auto imageAllocation = allocator.createImage(
+				imageCreateInfo,
+				vma::AllocationCreateInfo(
+						vma::AllocationCreateFlags(),
+						vma::MemoryUsage::eGpuOnly,
+						vk::MemoryPropertyFlagBits::eDeviceLocal,
+						vk::MemoryPropertyFlagBits::eDeviceLocal,
+						0,
+						vma::Pool(),
+						nullptr
+				)
 		);
 
-		vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex));
-		device.bindImageMemory(image, memory, 0);
+		vk::Image image = imageAllocation.first;
+		vma::Allocation allocation = imageAllocation.second;
 
 		vk::ImageAspectFlags aspectFlags;
 		
@@ -196,6 +177,8 @@ namespace vkcv {
 			aspectFlags = vk::ImageAspectFlagBits::eColor;
 		}
 		
+		const vk::Device& device = m_core->getContext().getDevice();
+		
 		std::vector<vk::ImageView> views;
 		for (uint32_t mip = 0; mip < mipCount; mip++) {
 			const vk::ImageViewCreateInfo imageViewCreateInfo(
@@ -222,11 +205,11 @@ namespace vkcv {
 		}
 		
 		const uint64_t id = m_images.size();
-		m_images.push_back(Image(image, memory, views, width, height, depth, format, arrayLayers));
+		m_images.push_back({ image, allocation, views, width, height, depth, format, arrayLayers, vk::ImageLayout::eUndefined });
 		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
 	}
 	
-	ImageHandle ImageManager::createSwapchainImage() {
+	ImageHandle ImageManager::createSwapchainImage() const {
 		return ImageHandle::createSwapchainImageHandle();
 	}
 	
@@ -262,10 +245,16 @@ namespace vkcv {
 		
 		auto& image = m_images[id];
 		
-		return image.m_memory;
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
+		
+		auto info = allocator.getAllocationInfo(
+				image.m_allocation
+		);
+		
+		return info.deviceMemory;
 	}
 	
-	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, const size_t mipLevel) const {
+	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, size_t mipLevel) const {
 		
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0];
@@ -626,15 +615,14 @@ namespace vkcv {
 				view = nullptr;
 			}
 		}
-
-		if (image.m_memory) {
-			device.freeMemory(image.m_memory);
-			image.m_memory = nullptr;
-		}
+		
+		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 
 		if (image.m_handle) {
-			device.destroyImage(image.m_handle);
+			allocator.destroyImage(image.m_handle, image.m_allocation);
+			
 			image.m_handle = nullptr;
+			image.m_allocation = nullptr;
 		}
 	}
 
@@ -670,11 +658,11 @@ namespace vkcv {
 		m_currentSwapchainInputImage = index;
 	}
 
-	void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, 
+	void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, const std::vector<vk::ImageView>& views,
 		uint32_t width, uint32_t height, vk::Format format) {
 
 		// destroy old views
-		for (auto image : m_swapchainImages) {
+		for (const auto& image : m_swapchainImages) {
 			for (const auto& view : image.m_viewPerMip) {
 				m_core->getContext().getDevice().destroyImageView(view);
 			}
@@ -683,7 +671,17 @@ namespace vkcv {
 		assert(images.size() == views.size());
 		m_swapchainImages.clear();
 		for (size_t i = 0; i < images.size(); i++) {
-			m_swapchainImages.push_back(Image(images[i], nullptr, { views[i] }, width, height, 1, format, 1));
+			m_swapchainImages.push_back({
+				images[i],
+				nullptr,
+				{ views[i] },
+				width,
+				height,
+				1,
+				format,
+				1,
+				vk::ImageLayout::eUndefined
+			});
 		}
 	}
 
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 9edd7471..646b3211 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -6,6 +6,7 @@
  */
 #include <vector>
 #include <vulkan/vulkan.hpp>
+#include <vk_mem_alloc.hpp>
 
 #include "vkcv/BufferManager.hpp"
 #include "vkcv/Handles.hpp"
@@ -20,28 +21,16 @@ namespace vkcv {
 		struct Image
 		{
 			vk::Image                   m_handle;
-			vk::DeviceMemory            m_memory;
+			vma::Allocation             m_allocation;
 			std::vector<vk::ImageView>  m_viewPerMip;
-			uint32_t                    m_width     = 0;
-			uint32_t                    m_height    = 0;
-			uint32_t                    m_depth     = 0;
+			uint32_t                    m_width;
+			uint32_t                    m_height;
+			uint32_t                    m_depth;
 			vk::Format                  m_format;
-			uint32_t                    m_layers    = 1;
-			vk::ImageLayout             m_layout    = vk::ImageLayout::eUndefined;
+			uint32_t                    m_layers;
+			vk::ImageLayout             m_layout;
 		private:
-			// struct is public so utility functions can access members, but only ImageManager can create Image
 			friend ImageManager;
-			Image(
-				vk::Image                   handle,
-				vk::DeviceMemory            memory,
-				std::vector<vk::ImageView>  views,
-				uint32_t                    width,
-				uint32_t                    height,
-				uint32_t                    depth,
-				vk::Format                  format,
-				uint32_t                    layers);
-
-			Image();
 		};
 	private:
 		
@@ -52,7 +41,7 @@ namespace vkcv {
 		std::vector<Image> m_swapchainImages;
 		int m_currentSwapchainInputImage;
 		
-		ImageManager(BufferManager& bufferManager) noexcept;
+		explicit ImageManager(BufferManager& bufferManager) noexcept;
 		
 		/**
 		 * Destroys and deallocates image represented by a given
@@ -82,7 +71,8 @@ namespace vkcv {
 			bool            supportColorAttachment,
 			Multisampling   msaa);
 		
-		ImageHandle createSwapchainImage();
+		[[nodiscard]]
+		ImageHandle createSwapchainImage() const;
 		
 		[[nodiscard]]
 		vk::Image getVulkanImage(const ImageHandle& handle) const;
@@ -91,7 +81,7 @@ namespace vkcv {
 		vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const;
 		
 		[[nodiscard]]
-		vk::ImageView getVulkanImageView(const ImageHandle& handle, const size_t mipLevel = 0) const;
+		vk::ImageView getVulkanImageView(const ImageHandle& handle, size_t mipLevel = 0) const;
 
 		void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout);
 		void recordImageLayoutTransition(
@@ -124,8 +114,9 @@ namespace vkcv {
 		uint32_t getImageMipCount(const ImageHandle& handle) const;
 
 		void setCurrentSwapchainImageIndex(int index);
-		void setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views,
-			uint32_t width, uint32_t height, vk::Format format);
+		
+		void setSwapchainImages(const std::vector<vk::Image>& images, const std::vector<vk::ImageView>& views,
+								uint32_t width, uint32_t height, vk::Format format);
 
 	};
 }
\ No newline at end of file
-- 
GitLab