Browse Source

Vulkan: GPU memory management

Panagiotis Christopoulos Charitos 9 years ago
parent
commit
49f2a8350f

+ 0 - 1
include/anki/gr/Enums.h

@@ -14,7 +14,6 @@ namespace anki
 
 /// @addtogroup graphics
 /// @{
-
 enum ColorBit : U8
 {
 	NONE = 0,

+ 85 - 0
include/anki/gr/vulkan/GpuMemoryAllocator.h

@@ -0,0 +1,85 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/Common.h>
+#include <anki/util/List.h>
+
+namespace anki
+{
+
+// Forward
+class GpuMemoryAllocatorChunk;
+
+/// @addtorgoup vulkan
+/// @{
+
+/// The handle that is returned from GpuMemoryAllocator's allocations.
+class GpuMemoryAllocationHandle
+{
+	friend class GpuMemoryAllocator;
+
+public:
+	VkDeviceMemory m_memory;
+	PtrSize m_offset;
+
+private:
+	GpuMemoryAllocatorChunk* m_chunk;
+};
+
+/// GPU memory allocator.
+class GpuMemoryAllocator
+{
+public:
+	GpuMemoryAllocator()
+	{
+	}
+
+	~GpuMemoryAllocator();
+
+	void init(GenericMemoryPoolAllocator<U8> alloc,
+		VkDevice dev,
+		U memoryTypeIdx,
+		PtrSize chunkInitialSize,
+		F32 nextChunkScale,
+		PtrSize nextChunkBias);
+
+	/// Allocate GPU memory.
+	void allocate(PtrSize size, U alignment, GpuMemoryAllocationHandle& handle);
+
+	/// Free allocated memory.
+	void free(const GpuMemoryAllocationHandle& handle);
+
+private:
+	using Chunk = GpuMemoryAllocatorChunk;
+
+	GenericMemoryPoolAllocator<U8> m_alloc;
+
+	VkDevice m_dev = VK_NULL_HANDLE;
+	U32 m_memIdx;
+
+	IntrusiveList<Chunk> m_activeChunks;
+	Mutex m_mtx;
+
+	/// Size of the first chunk.
+	PtrSize m_initSize = 0;
+
+	/// Chunk scale.
+	F32 m_scale = 2.0;
+
+	/// Chunk bias.
+	PtrSize m_bias = 0;
+
+	void createNewChunk();
+
+	Bool allocateFromChunk(PtrSize size,
+		U alignment,
+		Chunk& ch,
+		GpuMemoryAllocationHandle& handle);
+};
+/// @}
+
+} // end namespace anki

+ 17 - 0
include/anki/gr/vulkan/GrManagerImpl.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/Common.h>
+#include <anki/gr/vulkan/GpuMemoryAllocator.h>
 
 namespace anki
 {
@@ -18,6 +19,7 @@ class GrManagerImpl
 {
 public:
 	VkInstance m_instance = VK_NULL_HANDLE;
+	VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
 	VkDevice m_device = VK_NULL_HANDLE;
 	VkQueue m_queue = VK_NULL_HANDLE;
 	VkDescriptorSetLayout m_globalDescriptorSetLayout = VK_NULL_HANDLE;
@@ -44,8 +46,23 @@ private:
 	class CompatibleRenderPassHashMap;
 	CompatibleRenderPassHashMap* m_renderPasses = nullptr;
 
+	/// @name Memory
+	/// @{
+	VkPhysicalDeviceMemoryProperties m_memoryProperties;
+
+	/// One for each mem type.
+	DynamicArray<GpuMemoryAllocator> m_gpuMemAllocs;
+	/// @}
+
 	void initGlobalDsetLayout();
 	void initGlobalPplineLayout();
+
+	void initMemory();
+
+	/// Find a suitable memory type.
+	U findMemoryType(U resourceMemTypeBits,
+		VkMemoryPropertyFlags preferFlags,
+		VkMemoryPropertyFlags avoidFlags) const;
 };
 /// @}
 

+ 150 - 0
src/gr/vulkan/GpuMemoryAllocator.cpp

@@ -0,0 +1,150 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/GpuMemoryAllocator.h>
+
+namespace anki
+{
+
+//==============================================================================
+// GpuMemoryAllocatorChunk                                                     =
+//==============================================================================
+class GpuMemoryAllocatorChunk
+	: public IntrusiveListEnabled<GpuMemoryAllocatorChunk>
+{
+public:
+	/// GPU mem.
+	VkDeviceMemory m_mem;
+
+	/// Size of the allocation.
+	PtrSize m_size;
+
+	/// Current offset.
+	PtrSize m_offset;
+
+	/// Number of allocations.
+	U32 m_allocationCount;
+};
+
+//==============================================================================
+// GpuMemoryAllocator                                                          =
+//==============================================================================
+
+//==============================================================================
+GpuMemoryAllocator::~GpuMemoryAllocator()
+{
+}
+
+//==============================================================================
+void GpuMemoryAllocator::init(GenericMemoryPoolAllocator<U8> alloc,
+	VkDevice dev,
+	U memoryTypeIdx,
+	PtrSize chunkInitialSize,
+	F32 nextChunkScale,
+	PtrSize nextChunkBias)
+{
+	m_alloc = alloc;
+	m_dev = dev;
+	m_memIdx = memoryTypeIdx;
+	m_initSize = chunkInitialSize;
+	m_scale = nextChunkScale;
+	m_bias = nextChunkBias;
+}
+
+//==============================================================================
+Bool GpuMemoryAllocator::allocateFromChunk(
+	PtrSize size, U alignment, Chunk& ch, GpuMemoryAllocationHandle& handle)
+{
+	alignRoundUp(alignment, ch.m_offset);
+	if(ch.m_offset + size <= ch.m_size)
+	{
+		++ch.m_allocationCount;
+
+		handle.m_memory = ch.m_mem;
+		handle.m_offset = ch.m_offset;
+		handle.m_chunk = &ch;
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+//==============================================================================
+void GpuMemoryAllocator::createNewChunk()
+{
+	// Determine new chunk size
+	PtrSize newChunkSize;
+	if(!m_activeChunks.isEmpty())
+	{
+		newChunkSize = m_activeChunks.getBack().m_size * m_scale + m_bias;
+	}
+	else
+	{
+		newChunkSize = m_initSize;
+	}
+
+	Chunk* chunk = m_alloc.newInstance<Chunk>();
+	chunk->m_size = newChunkSize;
+	chunk->m_offset = 0;
+	chunk->m_allocationCount = 0;
+
+	VkMemoryAllocateInfo inf;
+	inf.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+	inf.pNext = nullptr;
+	inf.allocationSize = newChunkSize;
+	inf.memoryTypeIndex = m_memIdx;
+
+	if(vkAllocateMemory(m_dev, &inf, nullptr, &chunk->m_mem))
+	{
+		ANKI_LOGF("Out of GPU memory");
+	}
+
+	m_activeChunks.pushBack(chunk);
+}
+
+//==============================================================================
+void GpuMemoryAllocator::allocate(
+	PtrSize size, U alignment, GpuMemoryAllocationHandle& handle)
+{
+	handle.m_memory = VK_NULL_HANDLE;
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	if(m_activeChunks.isEmpty()
+		|| allocateFromChunk(size, alignment, m_activeChunks.getBack(), handle))
+	{
+		createNewChunk();
+	}
+
+	if(handle.m_memory == VK_NULL_HANDLE)
+	{
+		Bool success = allocateFromChunk(
+			size, alignment, m_activeChunks.getBack(), handle);
+		(void)success;
+		ANKI_ASSERT(success && "The chunk should have space");
+	}
+}
+
+//==============================================================================
+void GpuMemoryAllocator::free(const GpuMemoryAllocationHandle& handle)
+{
+	ANKI_ASSERT(handle.m_memory && handle.m_chunk);
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	ANKI_ASSERT(handle.m_chunk->m_allocationCount > 0);
+
+	--handle.m_chunk->m_allocationCount;
+	if(handle.m_chunk->m_allocationCount == 0)
+	{
+		m_activeChunks.erase(handle.m_chunk);
+		vkFreeMemory(m_dev, handle.m_chunk->m_mem, nullptr);
+		m_alloc.deleteInstance(handle.m_chunk);
+	}
+}
+
+} // end namespace anki

+ 53 - 0
src/gr/vulkan/GrManagerImpl.cpp

@@ -73,6 +73,7 @@ Error GrManagerImpl::init()
 {
 	initGlobalDsetLayout();
 	initGlobalPplineLayout();
+	initMemory();
 
 	return ErrorCode::NONE;
 }
@@ -268,4 +269,56 @@ VkRenderPass GrManagerImpl::getOrCreateCompatibleRenderPass(
 	return out;
 }
 
+//==============================================================================
+void GrManagerImpl::initMemory()
+{
+	vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);
+
+	m_gpuMemAllocs.create(m_alloc, m_memoryProperties.memoryTypeCount);
+	U idx = 0;
+	for(GpuMemoryAllocator& alloc : m_gpuMemAllocs)
+	{
+		alloc.init(m_alloc, m_device, idx++, 50 * 1024 * 1024, 1.0, 0);
+	}
+}
+
+//==============================================================================
+U GrManagerImpl::findMemoryType(U resourceMemTypeBits,
+	VkMemoryPropertyFlags preferFlags,
+	VkMemoryPropertyFlags avoidFlags) const
+{
+	U preferedHigh = MAX_U32;
+	U preferedMed = MAX_U32;
+
+	// Iterate all mem types
+	for(U i = 0; i < m_memoryProperties.memoryTypeCount; i++)
+	{
+		if(resourceMemTypeBits & (1u << i))
+		{
+			VkMemoryPropertyFlags flags =
+				m_memoryProperties.memoryTypes[i].propertyFlags;
+
+			if((flags & preferFlags) == preferFlags)
+			{
+				preferedMed = i;
+
+				if((flags & avoidFlags) != avoidFlags)
+				{
+					preferedHigh = i;
+				}
+			}
+		}
+	}
+
+	if(preferedHigh < MAX_U32)
+	{
+		return preferedHigh;
+	}
+	else
+	{
+		ANKI_ASSERT(preferedMed < MAX_U32);
+		return preferedMed;
+	}
+}
+
 } // end namespace anki