Browse Source

Added a Group memory allocator
More Vulkan fixes

BearishSun 9 years ago
parent
commit
903f96c9b7

+ 1 - 0
Source/BansheeUtility/CMakeSources.cmake

@@ -129,6 +129,7 @@ set(BS_BANSHEEUTILITY_INC_ALLOCATORS
 	"Include/BsMemoryAllocator.h"
 	"Include/BsMemStack.h"
 	"Include/BsStaticAlloc.h"
+	"Include/BsGroupAlloc.h"
 )
 
 set(BS_BANSHEEUTILITY_INC_THIRDPARTY

+ 188 - 0
Source/BansheeUtility/Include/BsGroupAlloc.h

@@ -0,0 +1,188 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+
+namespace bs
+{
+	/** @addtogroup Internal-Utility
+	 *  @{
+	 */
+
+	/** @addtogroup Memory-Internal
+	 *  @{
+	 */
+
+	/**
+	 * Provides an easy way to group multiple allocations under a single (actual) allocation. Requires the user to first
+	 * call reserve() methods for all requested data elements, followed by init(), after which allocation/deallocation
+	 * can follow using construct/destruct or alloc/free methods.
+	 */
+	class GroupAlloc : INonCopyable
+	{
+	public:
+		GroupAlloc()
+			: mData(nullptr), mDataPtr(nullptr), mNumBytes(0)
+		{ }
+
+		GroupAlloc(GroupAlloc&& other) noexcept
+			: mData(other.mData), mDataPtr(other.mDataPtr), mNumBytes(other.mNumBytes)
+		{
+			other.mData = nullptr;
+			other.mDataPtr = nullptr;
+			other.mNumBytes = 0;
+		}
+
+		~GroupAlloc()
+		{
+			if (mNumBytes > 0)
+				bs_free(mData);
+		}
+
+		GroupAlloc& operator=(GroupAlloc&& other) noexcept
+		{
+			if (this == &other)
+				return *this;
+
+			if (mNumBytes > 0)
+				bs_free(mData);
+
+			mData = other.mData;
+			mDataPtr = other.mDataPtr;
+			mNumBytes = other.mNumBytes;
+
+			other.mData = nullptr;
+			other.mDataPtr = nullptr;
+			other.mNumBytes = 0;
+
+			return *this;
+		}
+
+		/** 
+		 * Allocates internal memory as reserved by previous calls to reserve(). Must be called before any calls to
+		 * construct or alloc. 
+		 */
+		void init()
+		{
+			assert(mData == nullptr);
+
+			if (mNumBytes > 0)
+				mData = (UINT8*)bs_alloc(mNumBytes);
+
+			mDataPtr = mData;
+		}
+
+		/** 
+		 * Reserves the specified amount of bytes to allocate. Multiple calls to reserve() are cumulative. After all needed
+		 * memory is reserved, call init(), followed by actual allocation via construct() or alloc() methods.
+		 */
+		GroupAlloc& reserve(UINT32 amount)
+		{
+			assert(mData == nullptr);
+
+			mNumBytes += amount;
+			return *this;
+		}
+
+		/** 
+		 * Reserves the specified amount of bytes to allocate. Multiple calls to reserve() are cumulative. After all needed
+		 * memory is reserved, call init(), followed by actual allocation via construct() or alloc() methods.
+		 */
+		template<class T>
+		GroupAlloc& reserve(UINT32 count = 1)
+		{
+			assert(mData == nullptr);
+
+			mNumBytes += sizeof(T) * count;
+			return *this;
+		}
+
+		/**
+		 * Allocates a new piece of memory of the specified size.
+		 *
+		 * @param[in]	amount	Amount of memory to allocate, in bytes.
+		 */
+		UINT8* alloc(UINT32 amount)
+		{
+			assert(mDataPtr + amount <= (mData + mNumBytes));
+
+			UINT8* output = mDataPtr;
+			mDataPtr += amount;
+
+			return output;
+		}
+
+		/**
+		 * Allocates enough memory to hold @p count elements of the specified type.
+		 *
+		 * @param[in]	count	Number of elements to allocate.
+		 */
+		template<class T>
+		T* alloc(UINT32 count = 1)
+		{
+			return (T*)alloc(sizeof(T) * count);
+		}
+
+		/** Deallocates a previously allocated piece of memory. */
+		void free(void* data)
+		{
+			// Do nothing
+		}
+
+		/**
+		 * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
+		 */
+		template<class T>
+		T* construct(UINT32 count = 1)
+		{
+			T* data = (T*)alloc(sizeof(T) * count);
+
+			for(unsigned int i = 0; i < count; i++)
+				new ((void*)&data[i]) T;
+
+			return data;
+		}
+
+		/**
+		 * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
+		 */
+		template<class T, class... Args>
+		T* construct(Args &&...args, UINT32 count = 1)
+		{
+			T* data = (T*)alloc(sizeof(T) * count);
+
+			for(unsigned int i = 0; i < count; i++)
+				new ((void*)&data[i]) T(std::forward<Args>(args)...);
+
+			return data;
+		}
+
+		/** Destructs and deallocates an object allocated with the static allocator. */
+		template<class T>
+		void destruct(T* data)
+		{
+			data->~T();
+
+			free(data);
+		}
+
+		/** Destructs and deallocates an array of objects allocated with the static frame allocator. */
+		template<class T>
+		void destruct(T* data, UINT32 count)
+		{
+			for(unsigned int i = 0; i < count; i++)
+				data[i].~T();
+
+			free(data);
+		}
+
+	private:
+		UINT8* mData;
+		UINT8* mDataPtr;
+		UINT32 mNumBytes;
+	};
+
+	/** @} */
+	/** @} */
+}

+ 4 - 3
Source/BansheeVulkanRenderAPI/Include/BsVulkanVertexInputManager.h

@@ -3,6 +3,7 @@
 #pragma once
 
 #include "BsVulkanPrerequisites.h"
+#include "BsGroupAlloc.h"
 #include "BsModule.h"
 
 namespace bs
@@ -58,12 +59,12 @@ namespace bs
 		/**	Contains data about a single instance of vertex input object. */
 		struct VertexInputEntry
 		{
-			VertexInputEntry() {}
-
 			VkVertexInputAttributeDescription* attributes;
 			VkVertexInputBindingDescription* bindings;
 			SPtr<VulkanVertexInput> vertexInput;
 			UINT32 lastUsedIdx;
+
+			GroupAlloc allocator;
 		};
 
 	public:
@@ -91,7 +92,7 @@ namespace bs
 		static const int DECLARATION_BUFFER_SIZE = 1024;
 		static const int NUM_ELEMENTS_TO_PRUNE = 64;
 
-		UnorderedMap<VertexDeclarationKey, VertexInputEntry*, HashFunc, EqualFunc> mVertexInputMap;
+		UnorderedMap<VertexDeclarationKey, VertexInputEntry, HashFunc, EqualFunc> mVertexInputMap;
 
 		UINT32 mNextId;
 		bool mWarningShown;

+ 1 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp

@@ -469,7 +469,7 @@ namespace bs
 
 			VkPipelineShaderStageCreateInfo& stageCI = mShaderStageInfos[stageOutputIdx];
 
-			VulkanShaderModule* module = program->getShaderModule(i);
+			VulkanShaderModule* module = program->getShaderModule(deviceIdx);
 
 			if (module != nullptr)
 				stageCI.module = module->getHandle();

+ 18 - 29
Source/BansheeVulkanRenderAPI/Source/BsVulkanVertexInputManager.cpp

@@ -47,10 +47,6 @@ namespace bs
 		while (mVertexInputMap.begin() != mVertexInputMap.end())
 		{
 			auto firstElem = mVertexInputMap.begin();
-
-			firstElem->second->vertexInput.~SPtr<VulkanVertexInput>();
-			bs_free(firstElem->second);
-
 			mVertexInputMap.erase(firstElem);
 		}
 	}
@@ -75,8 +71,8 @@ namespace bs
 			iterFind = mVertexInputMap.find(pair);
 		}
 
-		iterFind->second->lastUsedIdx = ++mLastUsedCounter;
-		return iterFind->second->vertexInput;
+		iterFind->second.lastUsedIdx = ++mLastUsedCounter;
+		return iterFind->second.vertexInput;
 	}
 
 	void VulkanVertexInputManager::addNew(const SPtr<VertexDeclarationCore>& vbDecl, 
@@ -107,24 +103,19 @@ namespace bs
 			numBindings = std::max(numBindings, (UINT32)vbElem.getStreamIdx() + 1);
 		}
 
-		UINT32 attributesBytes = sizeof(VkVertexInputAttributeDescription) * numAttributes;
-		UINT32 bindingBytes = sizeof(VkVertexInputBindingDescription) * numBindings;
-		UINT32 totalBytes = sizeof(VertexInputEntry) + attributesBytes + bindingBytes;
+		VertexInputEntry newEntry;
+		GroupAlloc& alloc = newEntry.allocator;
 
-		UINT8* data = (UINT8*)bs_alloc(totalBytes);
-		VertexInputEntry* newEntry = (VertexInputEntry*)data;
-		new (&newEntry->vertexInput) SPtr<VulkanVertexInput>();
-		data += sizeof(VertexInputEntry);
+		alloc.reserve<VkVertexInputAttributeDescription>(numAttributes)
+			 .reserve<VkVertexInputBindingDescription>(numBindings)
+			 .init();
 
-		newEntry->attributes = (VkVertexInputAttributeDescription*)data;
-		data += attributesBytes;
-
-		newEntry->bindings = (VkVertexInputBindingDescription*)data;
-		data += bindingBytes;
+		newEntry.attributes = alloc.alloc<VkVertexInputAttributeDescription>(numAttributes);
+		newEntry.bindings = alloc.alloc<VkVertexInputBindingDescription>(numBindings);
 
 		for (UINT32 i = 0; i < numBindings; i++)
 		{
-			VkVertexInputBindingDescription& binding = newEntry->bindings[i];
+			VkVertexInputBindingDescription& binding = newEntry.bindings[i];
 			binding.binding = i;
 			binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
 			binding.stride = 0;
@@ -133,7 +124,7 @@ namespace bs
 		UINT32 attribIdx = 0;
 		for (auto& vbElem : vbElements)
 		{
-			VkVertexInputAttributeDescription& attribute = newEntry->attributes[attribIdx];
+			VkVertexInputAttributeDescription& attribute = newEntry.attributes[attribIdx];
 
 			bool foundSemantic = false;
 			for (auto& inputElem : inputElements)
@@ -153,7 +144,7 @@ namespace bs
 			attribute.format = VulkanUtility::getVertexType(vbElem.getType());
 			attribute.offset = vbElem.getOffset();
 
-			VkVertexInputBindingDescription& binding = newEntry->bindings[attribute.binding];
+			VkVertexInputBindingDescription& binding = newEntry.bindings[attribute.binding];
 
 			bool isPerVertex = vbElem.getInstanceStepRate() == 0;
 			bool isFirstInBinding = binding.stride == 0;
@@ -180,9 +171,9 @@ namespace bs
 		vertexInputCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
 		vertexInputCI.pNext = nullptr;
 		vertexInputCI.flags = 0;
-		vertexInputCI.pVertexBindingDescriptions = newEntry->bindings;
+		vertexInputCI.pVertexBindingDescriptions = newEntry.bindings;
 		vertexInputCI.vertexBindingDescriptionCount = numBindings;
-		vertexInputCI.pVertexAttributeDescriptions = newEntry->attributes;
+		vertexInputCI.pVertexAttributeDescriptions = newEntry.attributes;
 		vertexInputCI.vertexAttributeDescriptionCount = numAttributes;
 
 		// Create key and add to the layout map
@@ -191,10 +182,10 @@ namespace bs
 		pair.shaderDeclId = shaderInputDecl->getId();
 
 		Lock(mMutex);
-		newEntry->vertexInput = bs_shared_ptr_new<VulkanVertexInput>(mNextId++, vertexInputCI);
-		newEntry->lastUsedIdx = ++mLastUsedCounter;
+		newEntry.vertexInput = bs_shared_ptr_new<VulkanVertexInput>(mNextId++, vertexInputCI);
+		newEntry.lastUsedIdx = ++mLastUsedCounter;
 
-		mVertexInputMap[pair] = newEntry;
+		mVertexInputMap[pair] = std::move(newEntry);
 	}
 
 	void VulkanVertexInputManager::removeLeastUsed()
@@ -213,14 +204,12 @@ namespace bs
 		Map<UINT32, VertexDeclarationKey> leastFrequentlyUsedMap;
 
 		for (auto iter = mVertexInputMap.begin(); iter != mVertexInputMap.end(); ++iter)
-			leastFrequentlyUsedMap[iter->second->lastUsedIdx] = iter->first;
+			leastFrequentlyUsedMap[iter->second.lastUsedIdx] = iter->first;
 
 		UINT32 elemsRemoved = 0;
 		for (auto iter = leastFrequentlyUsedMap.begin(); iter != leastFrequentlyUsedMap.end(); ++iter)
 		{
 			auto inputLayoutIter = mVertexInputMap.find(iter->second);
-
-			bs_free(inputLayoutIter->second);
 			mVertexInputMap.erase(inputLayoutIter);
 
 			elemsRemoved++;