Descriptor sets update mechanism

This cl adds proper storage and update of descriptor sets.
The added functionality includes:
- The descriptor pool now allocates the proper larger required
  storage needed to actually store descriptor set data.
- Descriptor sets, when allocated, also get initialized with some
  basic header data (set layout) and also some Descriptor data
  when available (a.k.a: immutable samplers)
- Descriptors are currently bindless, since it is simpler as a first
  implementation, but can easily be modified, which is intended to
  be done in the near future. For now, each descriptor set is either
  a VkDescriptorImageInfo, a VkDescriptorBufferInfo or a VkBufferView
- Descriptors can be updated from either a VkWriteDescriptorSet or a
  VkCopyDescriptorSet structure. The update supports writing to
  multiple descriptor sets in a single operation and supports array
  sizes mismatch properly according to the spec

Bug b/123244275

Change-Id: I1e0430e0014e26a304632a4b2b10ad0f69b06180
Reviewed-on: https://swiftshader-review.googlesource.com/c/24910
Tested-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Vulkan/VkDescriptorPool.cpp b/src/Vulkan/VkDescriptorPool.cpp
index c95337a..1de234e 100644
--- a/src/Vulkan/VkDescriptorPool.cpp
+++ b/src/Vulkan/VkDescriptorPool.cpp
@@ -37,7 +37,9 @@
 
 	for(uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++)
 	{
-		size += pCreateInfo->pPoolSizes[i].descriptorCount * DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type);
+		size += pCreateInfo->pPoolSizes[i].descriptorCount *
+		        (sizeof(DescriptorSetLayout*) +
+		         DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type));
 	}
 
 	return size;
@@ -52,7 +54,15 @@
 		layoutSizes[i] = Cast(pSetLayouts[i])->getSize();
 	}
 
-	return allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets);
+	VkResult result = allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets);
+	if(result == VK_SUCCESS)
+	{
+		for(uint32_t i = 0; i < descriptorSetCount; i++)
+		{
+			Cast(pSetLayouts[i])->initialize(pDescriptorSets[i]);
+		}
+	}
+	return result;
 }
 
 VkDescriptorSet DescriptorPool::findAvailableMemory(size_t size)
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index cfd9340..d07e27b 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -13,11 +13,23 @@
 // limitations under the License.
 
 #include "VkDescriptorSetLayout.hpp"
+#include <algorithm>
 #include <cstring>
 
 namespace
 {
 
+struct DescriptorSet
+{
+	vk::DescriptorSetLayout* layout;
+	uint8_t data[];
+};
+
+static inline DescriptorSet* Cast(VkDescriptorSet object)
+{
+	return reinterpret_cast<DescriptorSet*>(object);
+}
+
 static bool UsesImmutableSamplers(const VkDescriptorSetLayoutBinding& binding)
 {
 	return (((binding.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) ||
@@ -33,16 +45,19 @@
 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);
+	uint8_t* hostMemory = static_cast<uint8_t*>(mem) + bindingCount * sizeof(VkDescriptorSetLayoutBinding);
+	bindingOffsets = reinterpret_cast<size_t*>(hostMemory);
+	hostMemory += bindingCount * sizeof(size_t);
 
+	size_t offset = 0;
 	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;
+			bindings[i].pImmutableSamplers = reinterpret_cast<const VkSampler*>(hostMemory);
+			hostMemory += immutableSamplersSize;
 			memcpy(const_cast<VkSampler*>(bindings[i].pImmutableSamplers),
 			       pCreateInfo->pBindings[i].pImmutableSamplers,
 			       immutableSamplersSize);
@@ -51,6 +66,8 @@
 		{
 			bindings[i].pImmutableSamplers = nullptr;
 		}
+		bindingOffsets[i] = offset;
+		offset += bindings[i].descriptorCount * GetDescriptorSize(bindings[i].descriptorType);
 	}
 }
 
@@ -71,7 +88,7 @@
 
 size_t DescriptorSetLayout::ComputeRequiredAllocationSize(const VkDescriptorSetLayoutCreateInfo* pCreateInfo)
 {
-	size_t allocationSize = pCreateInfo->bindingCount * sizeof(VkDescriptorSetLayoutBinding);
+	size_t allocationSize = pCreateInfo->bindingCount * (sizeof(VkDescriptorSetLayoutBinding) + sizeof(size_t));
 
 	for(uint32_t i = 0; i < pCreateInfo->bindingCount; i++)
 	{
@@ -92,14 +109,16 @@
 	case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
 	case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
 	case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+	case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+		return sizeof(VkDescriptorImageInfo);
 	case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
 	case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+		return sizeof(VkBufferView);
 	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
+		return sizeof(VkDescriptorBufferInfo);
 	default:
 		UNIMPLEMENTED("Unsupported Descriptor Type");
 	}
@@ -117,4 +136,119 @@
 	return size;
 }
 
+uint32_t DescriptorSetLayout::getBindingIndex(uint32_t binding) const
+{
+	for(uint32_t i = 0; i < bindingCount; i++)
+	{
+		if(binding == bindings[i].binding)
+		{
+			return i;
+		}
+	}
+
+	ASSERT(false); // Bindings should always be found
+	return 0;
+}
+
+void DescriptorSetLayout::initialize(VkDescriptorSet vkDescriptorSet)
+{
+	// Use a pointer to this descriptor set layout as the descriptor set's header
+	DescriptorSet* descriptorSet = ::Cast(vkDescriptorSet);
+	descriptorSet->layout = this;
+	uint8_t* mem = descriptorSet->data;
+
+	for(uint32_t i = 0; i < bindingCount; i++)
+	{
+		size_t typeSize = GetDescriptorSize(bindings[i].descriptorType);
+		if(UsesImmutableSamplers(bindings[i]))
+		{
+			for(uint32_t j = 0; j < bindings[i].descriptorCount; j++)
+			{
+				VkDescriptorImageInfo* imageInfo = reinterpret_cast<VkDescriptorImageInfo*>(mem);
+				imageInfo->sampler = bindings[i].pImmutableSamplers[j];
+				mem += typeSize;
+			}
+		}
+		else
+		{
+			mem += bindings[i].descriptorCount * typeSize;
+		}
+	}
+}
+
+uint8_t* DescriptorSetLayout::getOffsetPointer(VkDescriptorSet descriptorSet, uint32_t binding, uint32_t arrayElement, uint32_t count, size_t* typeSize) const
+{
+	uint32_t index = getBindingIndex(binding);
+	*typeSize = GetDescriptorSize(bindings[index].descriptorType);
+	size_t byteOffset = bindingOffsets[index] + (*typeSize * arrayElement);
+	ASSERT(((*typeSize * count) + byteOffset) <= getSize()); // Make sure the operation will not go out of bounds
+	return &(::Cast(descriptorSet)->data[byteOffset]);
+}
+
+const uint8_t* DescriptorSetLayout::GetInputData(const VkWriteDescriptorSet& descriptorWrites)
+{
+	switch(descriptorWrites.descriptorType)
+	{
+	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_INPUT_ATTACHMENT:
+		return reinterpret_cast<const uint8_t*>(descriptorWrites.pImageInfo);
+	case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+	case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+		return reinterpret_cast<const uint8_t*>(descriptorWrites.pTexelBufferView);
+		break;
+	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:
+		return reinterpret_cast<const uint8_t*>(descriptorWrites.pBufferInfo);
+		break;
+	default:
+		UNIMPLEMENTED();
+		return nullptr;
+	}
+}
+
+void DescriptorSetLayout::WriteDescriptorSet(const VkWriteDescriptorSet& descriptorWrites)
+{
+	DescriptorSet* dstSet = ::Cast(descriptorWrites.dstSet);
+	DescriptorSetLayout* dstLayout = dstSet->layout;
+	ASSERT(dstLayout);
+
+	size_t typeSize = 0;
+	uint8_t* memToWrite = dstLayout->getOffsetPointer(descriptorWrites.dstSet, descriptorWrites.dstBinding, descriptorWrites.dstArrayElement, descriptorWrites.descriptorCount, &typeSize);
+
+	// If the dstBinding has fewer than descriptorCount array elements remaining
+	// starting from dstArrayElement, then the remainder will be used to update
+	// the subsequent binding - dstBinding+1 starting at array element zero. If
+	// a binding has a descriptorCount of zero, it is skipped. This behavior
+	// applies recursively, with the update affecting consecutive bindings as
+	// needed to update all descriptorCount descriptors.
+	size_t writeSize = typeSize * descriptorWrites.descriptorCount;
+	memcpy(memToWrite, DescriptorSetLayout::GetInputData(descriptorWrites), writeSize);
+}
+
+void DescriptorSetLayout::CopyDescriptorSet(const VkCopyDescriptorSet& descriptorCopies)
+{
+	DescriptorSet* srcSet = ::Cast(descriptorCopies.srcSet);
+	DescriptorSetLayout* srcLayout = srcSet->layout;
+	ASSERT(srcLayout);
+
+	DescriptorSet* dstSet = ::Cast(descriptorCopies.dstSet);
+	DescriptorSetLayout* dstLayout = dstSet->layout;
+	ASSERT(dstLayout);
+
+	size_t srcTypeSize = 0;
+	uint8_t* memToRead = srcLayout->getOffsetPointer(descriptorCopies.srcSet, descriptorCopies.srcBinding, descriptorCopies.srcArrayElement, descriptorCopies.descriptorCount, &srcTypeSize);
+
+	size_t dstTypeSize = 0;
+	uint8_t* memToWrite = dstLayout->getOffsetPointer(descriptorCopies.dstSet, descriptorCopies.dstBinding, descriptorCopies.dstArrayElement, descriptorCopies.descriptorCount, &dstTypeSize);
+
+	ASSERT(srcTypeSize == dstTypeSize);
+	size_t writeSize = dstTypeSize * descriptorCopies.descriptorCount;
+	memcpy(memToWrite, memToRead, writeSize);
+}
+
 } // namespace vk
\ No newline at end of file
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 7c5f734..627c9ab 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -30,13 +30,21 @@
 	static size_t ComputeRequiredAllocationSize(const VkDescriptorSetLayoutCreateInfo* pCreateInfo);
 
 	static size_t GetDescriptorSize(VkDescriptorType type);
+	static void WriteDescriptorSet(const VkWriteDescriptorSet& descriptorWrites);
+	static void CopyDescriptorSet(const VkCopyDescriptorSet& descriptorCopies);
 
+	void initialize(VkDescriptorSet descriptorSet);
 	size_t getSize() const;
 
 private:
+	uint32_t getBindingIndex(uint32_t binding) const;
+	uint8_t* getOffsetPointer(VkDescriptorSet descriptorSet, uint32_t binding, uint32_t arrayElement, uint32_t count, size_t* typeSize) const;
+	static const uint8_t* GetInputData(const VkWriteDescriptorSet& descriptorWrites);
+
 	VkDescriptorSetLayoutCreateFlags flags;
 	uint32_t                         bindingCount;
 	VkDescriptorSetLayoutBinding*    bindings;
+	size_t*                          bindingOffsets;
 };
 
 static inline DescriptorSetLayout* Cast(VkDescriptorSetLayout object)
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index 4b3ae65..60e13f1 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -16,6 +16,7 @@
 
 #include "VkConfig.h"
 #include "VkDebug.hpp"
+#include "VkDescriptorSetLayout.hpp"
 #include "VkQueue.hpp"
 
 #include <new> // Must #include this to use "placement new"
@@ -99,4 +100,18 @@
 	pSupport->supported = VK_FALSE;
 }
 
+void Device::updateDescriptorSets(uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites,
+                                  uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies)
+{
+	for(uint32_t i = 0; i < descriptorWriteCount; i++)
+	{
+		DescriptorSetLayout::WriteDescriptorSet(pDescriptorWrites[i]);
+	}
+
+	for(uint32_t i = 0; i < descriptorCopyCount; i++)
+	{
+		DescriptorSetLayout::CopyDescriptorSet(pDescriptorCopies[i]);
+	}
+}
+
 } // namespace vk
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index 5eca001..24e0923 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -44,6 +44,8 @@
 	void getDescriptorSetLayoutSupport(const VkDescriptorSetLayoutCreateInfo* pCreateInfo,
 	                                   VkDescriptorSetLayoutSupport* pSupport) const;
 	VkPhysicalDevice getPhysicalDevice() const { return physicalDevice; }
+	void updateDescriptorSets(uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites,
+	                          uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies);
 
 private:
 	VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index d92aa7a..1caab01 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -1189,8 +1189,10 @@
 
 VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies)
 {
-	TRACE("()");
-	UNIMPLEMENTED();
+	TRACE("(VkDevice device = 0x%X, uint32_t descriptorWriteCount = %d, const VkWriteDescriptorSet* pDescriptorWrites = 0x%X, uint32_t descriptorCopyCount = %d, const VkCopyDescriptorSet* pDescriptorCopies = 0x%X)",
+	      device, descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies);
+
+	vk::Cast(device)->updateDescriptorSets(descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer)