Basic DescriptorPool and DescriptorSetLayout implementation

This cl is a minimal implementation of DescriptorPool and
DescriptorSetLayout.

A DescriptorSet itself is just a straight memory pointer.

A DescriptorPool is a memory pool where DescriptorSets are
allocated. The DescriptorPool object itself is little more
than a memory allocator.
A few minor optimizations were made to speed up the default
case:
- The Descriptor pool starts looking for a large enough
  memory block at the end of the pool, which should be optimal
  if the DescriptorSets are allocated linearly
- When allocating multiple DescriptorSets at once, the pool
  first attempts to allocated all of them contiguously. This
  prevents looking for free memory blocks multiple times and
  once again should be optimal if the DescriptorSets are
  allocated linearly

Note: For now, DescriptorSetLayout::GetDescriptorSize() is a
      dummy function which always returns the same size for
      every Descriptor type. This will be implemented properly
      case by case as we add support for new Descriptor types.

Passes all tests in:
dEQP-VK.api.*descriptor*

Bug b/123244275

Change-Id: I2a2e73396e38dae28b59b77243cd8a366b35c12c
Reviewed-on: https://swiftshader-review.googlesource.com/c/24028
Tested-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Vulkan/VkDescriptorPool.cpp b/src/Vulkan/VkDescriptorPool.cpp
new file mode 100644
index 0000000..c95337a
--- /dev/null
+++ b/src/Vulkan/VkDescriptorPool.cpp
@@ -0,0 +1,199 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "VkDescriptorPool.hpp"
+#include "VkDescriptorSetLayout.hpp"
+#include <algorithm>
+#include <memory>
+
+namespace vk
+{
+
+DescriptorPool::DescriptorPool(const VkDescriptorPoolCreateInfo* pCreateInfo, void* mem) :
+	pool(reinterpret_cast<VkDescriptorSet>(mem)),
+	poolSize(ComputeRequiredAllocationSize(pCreateInfo))
+{
+}
+
+void DescriptorPool::destroy(const VkAllocationCallbacks* pAllocator)
+{
+	vk::deallocate(pool, pAllocator);
+}
+
+size_t DescriptorPool::ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo* pCreateInfo)
+{
+	size_t size = 0;
+
+	for(uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++)
+	{
+		size += pCreateInfo->pPoolSizes[i].descriptorCount * DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type);
+	}
+
+	return size;
+}
+
+VkResult DescriptorPool::allocateSets(uint32_t descriptorSetCount, const VkDescriptorSetLayout* pSetLayouts, VkDescriptorSet* pDescriptorSets)
+{
+	std::unique_ptr<size_t[]> layoutSizes(new size_t[descriptorSetCount]);
+	for(uint32_t i = 0; i < descriptorSetCount; i++)
+	{
+		pDescriptorSets[i] = VK_NULL_HANDLE;
+		layoutSizes[i] = Cast(pSetLayouts[i])->getSize();
+	}
+
+	return allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets);
+}
+
+VkDescriptorSet DescriptorPool::findAvailableMemory(size_t size)
+{
+	if(nodes.empty())
+	{
+		return pool;
+	}
+
+	// First, look for space at the end of the pool
+	const auto itLast = nodes.rbegin();
+	ptrdiff_t itemStart = reinterpret_cast<char*>(itLast->set) - reinterpret_cast<char*>(pool);
+	ptrdiff_t nextItemStart = itemStart + itLast->size;
+	size_t freeSpace = poolSize - nextItemStart;
+	if(freeSpace >= size)
+	{
+		return reinterpret_cast<VkDescriptorSet>(nextItemStart);
+	}
+
+	// Second, look for space at the beginning of the pool
+	const auto itBegin = nodes.end();
+	freeSpace = reinterpret_cast<char*>(itBegin->set) - reinterpret_cast<char*>(pool);
+	if(freeSpace >= size)
+	{
+		return pool;
+	}
+
+	// Finally, look between existing pool items
+	const auto itEnd = nodes.end();
+	auto nextIt = itBegin;
+	++nextIt;
+	for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
+	{
+		VkDescriptorSet freeSpaceStart = reinterpret_cast<VkDescriptorSet>(reinterpret_cast<char*>(it->set) + it->size);
+		freeSpace = reinterpret_cast<char*>(nextIt->set) - reinterpret_cast<char*>(freeSpaceStart);
+		if(freeSpace >= size)
+		{
+			return freeSpaceStart;
+		}
+	}
+
+	return VK_NULL_HANDLE;
+}
+
+VkResult DescriptorPool::allocateSets(size_t* sizes, uint32_t numAllocs, VkDescriptorSet* pDescriptorSets)
+{
+	size_t totalSize = 0;
+	for(uint32_t i = 0; i < numAllocs; i++)
+	{
+		totalSize += sizes[i];
+	}
+
+	if(totalSize > poolSize)
+	{
+		return VK_ERROR_OUT_OF_POOL_MEMORY;
+	}
+
+	// Attempt to allocate single chunk of memory
+	VkDescriptorSet memory = findAvailableMemory(totalSize);
+	if(memory != VK_NULL_HANDLE)
+	{
+		pDescriptorSets[0] = memory;
+		for(uint32_t i = 1; i < numAllocs; i++)
+		{
+			pDescriptorSets[i] =
+				reinterpret_cast<VkDescriptorSet>(reinterpret_cast<char*>(memory) + sizes[i - 1]);
+			nodes.insert(Node(pDescriptorSets[i], sizes[i]));
+		}
+		return VK_SUCCESS;
+	}
+
+	// Atttempt to allocate each descriptor set separately
+	for(uint32_t i = 0; i < numAllocs; i++)
+	{
+		pDescriptorSets[i] = findAvailableMemory(sizes[i]);
+		if(pDescriptorSets[i] == VK_NULL_HANDLE)
+		{
+			// vkAllocateDescriptorSets can be used to create multiple descriptor sets. If the
+			// creation of any of those descriptor sets fails, then the implementation must
+			// destroy all successfully created descriptor set objects from this command, set
+			// all entries of the pDescriptorSets array to VK_NULL_HANDLE and return the error.
+			for(uint32_t j = 0; j < i; j++)
+			{
+				freeSet(pDescriptorSets[j]);
+				pDescriptorSets[j] = VK_NULL_HANDLE;
+			}
+			return (computeTotalFreeSize() > totalSize) ? VK_ERROR_FRAGMENTED_POOL : VK_ERROR_OUT_OF_POOL_MEMORY;
+		}
+		nodes.insert(Node(pDescriptorSets[i], sizes[i]));
+	}
+
+	return VK_SUCCESS;
+}
+
+void DescriptorPool::freeSets(uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets)
+{
+	for(uint32_t i = 0; i < descriptorSetCount; i++)
+	{
+		freeSet(pDescriptorSets[i]);
+	}
+}
+
+void DescriptorPool::freeSet(const VkDescriptorSet descriptorSet)
+{
+	const auto itEnd = nodes.end();
+	auto it = std::find(nodes.begin(), itEnd, descriptorSet);
+	if(it != itEnd)
+	{
+		nodes.erase(it);
+	}
+}
+
+VkResult DescriptorPool::reset()
+{
+	nodes.clear();
+
+	return VK_SUCCESS;
+}
+
+size_t DescriptorPool::computeTotalFreeSize() const
+{
+	size_t totalFreeSize = 0;
+
+	// Compute space at the end of the pool
+	const auto itLast = nodes.rbegin();
+	totalFreeSize += poolSize - ((reinterpret_cast<char*>(itLast->set) - reinterpret_cast<char*>(pool)) + itLast->size);
+
+	// Compute space at the beginning of the pool
+	const auto itBegin = nodes.end();
+	totalFreeSize += reinterpret_cast<char*>(itBegin->set) - reinterpret_cast<char*>(pool);
+
+	// Finally, look between existing pool items
+	const auto itEnd = nodes.end();
+	auto nextIt = itBegin;
+	++nextIt;
+	for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
+	{
+		totalFreeSize += (reinterpret_cast<char*>(nextIt->set) - reinterpret_cast<char*>(it->set)) - it->size;
+	}
+
+	return totalFreeSize;
+}
+
+} // namespace vk
\ No newline at end of file
diff --git a/src/Vulkan/VkDescriptorPool.hpp b/src/Vulkan/VkDescriptorPool.hpp
new file mode 100644
index 0000000..baa64f9
--- /dev/null
+++ b/src/Vulkan/VkDescriptorPool.hpp
@@ -0,0 +1,64 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef VK_DESCRIPTOR_POOL_HPP_
+#define VK_DESCRIPTOR_POOL_HPP_
+
+#include "VkObject.hpp"
+#include <set>
+
+namespace vk
+{
+	class DescriptorPool : public Object<DescriptorPool, VkDescriptorPool>
+	{
+	public:
+		DescriptorPool(const VkDescriptorPoolCreateInfo* pCreateInfo, void* mem);
+		~DescriptorPool() = delete;
+		void destroy(const VkAllocationCallbacks* pAllocator);
+
+		static size_t ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo* pCreateInfo);
+
+		VkResult allocateSets(uint32_t descriptorSetCount, const VkDescriptorSetLayout* pSetLayouts, VkDescriptorSet* pDescriptorSets);
+		void freeSets(uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets);
+		VkResult reset();
+
+	private:
+		VkResult allocateSets(size_t* sizes, uint32_t numAllocs, VkDescriptorSet* pDescriptorSets);
+		VkDescriptorSet findAvailableMemory(size_t size);
+		void freeSet(const VkDescriptorSet descriptorSet);
+		size_t computeTotalFreeSize() const;
+
+		struct Node
+		{
+			Node(VkDescriptorSet set, size_t size) : set(set), size(size) {}
+			bool operator<(const Node& node) const { return this->set < node.set; }
+			bool operator==(VkDescriptorSet set) const { return this->set == set; }
+
+			VkDescriptorSet set;
+			size_t size;
+		};
+		std::set<Node> nodes;
+
+		VkDescriptorSet pool = nullptr;
+		size_t poolSize = 0;
+	};
+
+	static inline DescriptorPool* Cast(VkDescriptorPool object)
+	{
+		return reinterpret_cast<DescriptorPool*>(object);
+	}
+
+} // namespace vk
+
+#endif // VK_DESCRIPTOR_POOL_HPP_
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
new file mode 100644
index 0000000..cfd9340
--- /dev/null
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -0,0 +1,120 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "VkDescriptorSetLayout.hpp"
+#include <cstring>
+
+namespace
+{
+
+static bool UsesImmutableSamplers(const VkDescriptorSetLayoutBinding& binding)
+{
+	return (((binding.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) ||
+	        (binding.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)) &&
+	        (binding.pImmutableSamplers != nullptr));
+}
+
+}
+
+namespace vk
+{
+
+DescriptorSetLayout::DescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, void* mem) :
+	flags(pCreateInfo->flags), bindingCount(pCreateInfo->bindingCount), bindings(reinterpret_cast<VkDescriptorSetLayoutBinding*>(mem))
+{
+	char* host_memory = static_cast<char*>(mem) + bindingCount * sizeof(VkDescriptorSetLayoutBinding);
+
+	for(uint32_t i = 0; i < bindingCount; i++)
+	{
+		bindings[i] = pCreateInfo->pBindings[i];
+		if(UsesImmutableSamplers(bindings[i]))
+		{
+			size_t immutableSamplersSize = bindings[i].descriptorCount * sizeof(VkSampler);
+			bindings[i].pImmutableSamplers = reinterpret_cast<const VkSampler*>(host_memory);
+			host_memory += immutableSamplersSize;
+			memcpy(const_cast<VkSampler*>(bindings[i].pImmutableSamplers),
+			       pCreateInfo->pBindings[i].pImmutableSamplers,
+			       immutableSamplersSize);
+		}
+		else
+		{
+			bindings[i].pImmutableSamplers = nullptr;
+		}
+	}
+}
+
+void DescriptorSetLayout::destroy(const VkAllocationCallbacks* pAllocator)
+{
+	for(uint32_t i = 0; i < bindingCount; i++)
+	{
+		if(UsesImmutableSamplers(bindings[i]))
+		{
+			// A single allocation is used for all immutable samplers, so only a single deallocation is needed.
+			vk::deallocate(const_cast<VkSampler*>(bindings[i].pImmutableSamplers), pAllocator);
+			break;
+		}
+	}
+
+	vk::deallocate(bindings, pAllocator);
+}
+
+size_t DescriptorSetLayout::ComputeRequiredAllocationSize(const VkDescriptorSetLayoutCreateInfo* pCreateInfo)
+{
+	size_t allocationSize = pCreateInfo->bindingCount * sizeof(VkDescriptorSetLayoutBinding);
+
+	for(uint32_t i = 0; i < pCreateInfo->bindingCount; i++)
+	{
+		if(UsesImmutableSamplers(pCreateInfo->pBindings[i]))
+		{
+			allocationSize += pCreateInfo->pBindings[i].descriptorCount * sizeof(VkSampler);
+		}
+	}
+
+	return allocationSize;
+}
+
+size_t DescriptorSetLayout::GetDescriptorSize(VkDescriptorType type)
+{
+	switch(type)
+	{
+	case VK_DESCRIPTOR_TYPE_SAMPLER:
+	case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+	case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+	case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+	case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+	case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+	case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+	case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+	case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+	case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+	case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+		return sizeof(void*); // FIXME(b/123244275) : Compute actual required size for each desciptor type
+	default:
+		UNIMPLEMENTED("Unsupported Descriptor Type");
+	}
+
+	return 0;
+}
+
+size_t DescriptorSetLayout::getSize() const
+{
+	size_t size = 0;
+	for(uint32_t i = 0; i < bindingCount; i++)
+	{
+		size += bindings[i].descriptorCount * GetDescriptorSize(bindings[i].descriptorType);
+	}
+	return size;
+}
+
+} // namespace vk
\ No newline at end of file
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
new file mode 100644
index 0000000..7c5f734
--- /dev/null
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -0,0 +1,49 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef VK_DESCRIPTOR_SET_LAYOUT_HPP_
+#define VK_DESCRIPTOR_SET_LAYOUT_HPP_
+
+#include "VkObject.hpp"
+
+namespace vk
+{
+
+class DescriptorSetLayout : public Object<DescriptorSetLayout, VkDescriptorSetLayout>
+{
+public:
+	DescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo* pCreateInfo, void* mem);
+	~DescriptorSetLayout() = delete;
+	void destroy(const VkAllocationCallbacks* pAllocator);
+
+	static size_t ComputeRequiredAllocationSize(const VkDescriptorSetLayoutCreateInfo* pCreateInfo);
+
+	static size_t GetDescriptorSize(VkDescriptorType type);
+
+	size_t getSize() const;
+
+private:
+	VkDescriptorSetLayoutCreateFlags flags;
+	uint32_t                         bindingCount;
+	VkDescriptorSetLayoutBinding*    bindings;
+};
+
+static inline DescriptorSetLayout* Cast(VkDescriptorSetLayout object)
+{
+	return reinterpret_cast<DescriptorSetLayout*>(object);
+}
+
+} // namespace vk
+
+#endif // VK_DESCRIPTOR_SET_LAYOUT_HPP_
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index a8959d0..66c6a3e 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -18,6 +18,8 @@
 #include "VkCommandPool.hpp"
 #include "VkConfig.h"
 #include "VkDebug.hpp"
+#include "VkDescriptorPool.hpp"
+#include "VkDescriptorSetLayout.hpp"
 #include "VkDestroy.h"
 #include "VkDevice.hpp"
 #include "VkDeviceMemory.hpp"
@@ -1054,48 +1056,80 @@
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
-	return VK_SUCCESS;
+	TRACE("(VkDevice device = 0x%X, const VkDescriptorSetLayoutCreateInfo* pCreateInfo = 0x%X, const VkAllocationCallbacks* pAllocator = 0x%X, VkDescriptorSetLayout* pSetLayout = 0x%X)",
+	      device, pCreateInfo, pAllocator, pSetLayout);
+
+	if(pCreateInfo->pNext)
+	{
+		UNIMPLEMENTED();
+	}
+
+	return vk::DescriptorSetLayout::Create(pAllocator, pCreateInfo, pSetLayout);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
+	TRACE("(VkDevice device = 0x%X, VkDescriptorSetLayout descriptorSetLayout = 0x%X, const VkAllocationCallbacks* pAllocator = 0x%X)",
+	      device, descriptorSetLayout, pAllocator);
+
+	vk::destroy(descriptorSetLayout, pAllocator);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
-	return VK_SUCCESS;
+	TRACE("(VkDevice device = 0x%X, const VkDescriptorPoolCreateInfo* pCreateInfo = 0x%X, const VkAllocationCallbacks* pAllocator = 0x%X, VkDescriptorPool* pDescriptorPool = 0x%X)",
+	      device, pCreateInfo, pAllocator, pDescriptorPool);
+
+	if(pCreateInfo->pNext)
+	{
+		UNIMPLEMENTED();
+	}
+
+	return vk::DescriptorPool::Create(pAllocator, pCreateInfo, pDescriptorPool);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
+	TRACE("(VkDevice device = 0x%X, VkDescriptorPool descriptorPool = 0x%X, const VkAllocationCallbacks* pAllocator = 0x%X)",
+	      device, descriptorPool, pAllocator);
+
+	vk::destroy(descriptorPool, pAllocator);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
-	return VK_SUCCESS;
+	TRACE("(VkDevice device = 0x%X, VkDescriptorPool descriptorPool = 0x%X, VkDescriptorPoolResetFlags flags = 0x%X)",
+		device, descriptorPool, flags);
+
+	if(flags)
+	{
+		UNIMPLEMENTED();
+	}
+
+	return vk::Cast(descriptorPool)->reset();
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
-	return VK_SUCCESS;
+	TRACE("(VkDevice device = 0x%X, const VkDescriptorSetAllocateInfo* pAllocateInfo = 0x%X, VkDescriptorSet* pDescriptorSets = 0x%X)",
+		device, pAllocateInfo, pDescriptorSets);
+
+	if(pAllocateInfo->pNext)
+	{
+		UNIMPLEMENTED();
+	}
+
+	return vk::Cast(pAllocateInfo->descriptorPool)->allocateSets(
+		pAllocateInfo->descriptorSetCount, pAllocateInfo->pSetLayouts, pDescriptorSets);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
+	TRACE("(VkDevice device = 0x%X, VkDescriptorPool descriptorPool = 0x%X, uint32_t descriptorSetCount = %d, const VkDescriptorSet* pDescriptorSets = 0x%X)",
+		device, descriptorPool, descriptorSetCount, pDescriptorSets);
+
+	vk::Cast(descriptorPool)->freeSets(descriptorSetCount, pDescriptorSets);
+
 	return VK_SUCCESS;
 }
 
diff --git a/src/Vulkan/vulkan.vcxproj b/src/Vulkan/vulkan.vcxproj
index 0bd3618..275b383 100644
--- a/src/Vulkan/vulkan.vcxproj
+++ b/src/Vulkan/vulkan.vcxproj
@@ -104,6 +104,8 @@
     <ClCompile Include="VkCommandBuffer.cpp" />

     <ClCompile Include="VkCommandPool.cpp" />

     <ClCompile Include="VkDebug.cpp" />

+    <ClCompile Include="VkDescriptorPool.cpp" />

+    <ClCompile Include="VkDescriptorSetLayout.cpp" />

     <ClCompile Include="VkDevice.cpp" />

     <ClCompile Include="VkDeviceMemory.cpp" />

     <ClCompile Include="VkFramebuffer.cpp" />

@@ -198,6 +200,8 @@
     <ClInclude Include="VkCommandPool.hpp" />

     <ClInclude Include="VkConfig.h" />

     <ClInclude Include="VkDebug.hpp" />

+    <ClInclude Include="VkDescriptorPool.hpp" />

+    <ClInclude Include="VkDescriptorSetLayout.hpp" />

     <ClInclude Include="VkDestroy.h" />

     <ClInclude Include="VkDevice.hpp" />

     <ClInclude Include="VkDeviceMemory.hpp" />

diff --git a/src/Vulkan/vulkan.vcxproj.filters b/src/Vulkan/vulkan.vcxproj.filters
index f3fbdbd..2d332a8 100644
--- a/src/Vulkan/vulkan.vcxproj.filters
+++ b/src/Vulkan/vulkan.vcxproj.filters
@@ -210,6 +210,12 @@
     <ClCompile Include="VkDebug.cpp">

       <Filter>Source Files\Vulkan</Filter>

     </ClCompile>

+    <ClCompile Include="VkDescriptorPool.cpp">

+      <Filter>Source Files\Vulkan</Filter>

+    </ClCompile>

+    <ClCompile Include="VkDescriptorSetLayout.cpp">

+      <Filter>Source Files\Vulkan</Filter>

+    </ClCompile>

     <ClCompile Include="VkDevice.cpp">

       <Filter>Source Files\Vulkan</Filter>

     </ClCompile>

@@ -281,6 +287,12 @@
     <ClInclude Include="VkConfig.h">

       <Filter>Header Files\Vulkan</Filter>

     </ClInclude>

+    <ClInclude Include="VkDescriptorPool.hpp">

+      <Filter>Header Files\Vulkan</Filter>

+    </ClInclude>

+    <ClInclude Include="VkDescriptorSetLayout.hpp">

+      <Filter>Header Files\Vulkan</Filter>

+    </ClInclude>

     <ClInclude Include="VkDevice.hpp">

       <Filter>Header Files\Vulkan</Filter>

     </ClInclude>