Implement VK_KHR_internally_synchronized_queues

Internally, the queues are almost already internally synchronized.  This
simple implementation adds a high-level lock at the entry-point level
just in case.

Test: dEQP-VK.synchronization2.internally_synchronized_queues.*
Change-Id: I3fcd1fba06662f2212db9008fdf9a3c9c7582de2
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/77408
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index cc58b08..6ead907 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -136,7 +136,7 @@
 
 		for(uint32_t j = 0; j < queueCreateInfo.queueCount; j++, queueID++)
 		{
-			new(&queues[queueID]) Queue(this, scheduler.get());
+			new(&queues[queueID]) Queue(this, scheduler.get(), queueCreateInfo.flags);
 		}
 	}
 
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 21dbcb1..5d5edcf 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -426,6 +426,12 @@
 }
 
 template<typename T>
+static void getPhysicalDeviceInternallySynchronizedQueuesFeatures(T *features)
+{
+	features->internallySynchronizedQueues = VK_TRUE;
+}
+
+template<typename T>
 static void getPhysicalDeviceVulkan12Features(T *features)
 {
 	features->samplerMirrorClampToEdge = VK_TRUE;
@@ -698,6 +704,9 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES:
 			getPhysicalDeviceIndexTypeUint8Features(reinterpret_cast<VkPhysicalDeviceIndexTypeUint8Features *>(curExtension));
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INTERNALLY_SYNCHRONIZED_QUEUES_FEATURES_KHR:
+			getPhysicalDeviceInternallySynchronizedQueuesFeatures(reinterpret_cast<VkPhysicalDeviceInternallySynchronizedQueuesFeaturesKHR *>(curExtension));
+			break;
 		case VK_STRUCTURE_TYPE_MAX_ENUM:  // TODO(b/176893525): This may not be legal. dEQP tests that this value is ignored.
 			break;
 		default:
@@ -1818,6 +1827,13 @@
 	return CheckFeature(requested, supported, indexTypeUint8);
 }
 
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceInternallySynchronizedQueuesFeaturesKHR *requested) const
+{
+	auto supported = getSupportedFeatures(requested);
+
+	return CheckFeature(requested, supported, internallySynchronizedQueues);
+}
+
 bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceDescriptorIndexingFeatures *requested) const
 {
 	auto supported = getSupportedFeatures(requested);
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index b750692..a12b8a6 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -67,6 +67,7 @@
 	bool hasExtendedFeatures(const VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR *requested) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceHostImageCopyFeatures *requested) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceIndexTypeUint8Features *requested) const;
+	bool hasExtendedFeatures(const VkPhysicalDeviceInternallySynchronizedQueuesFeaturesKHR *requested) const;
 
 	const VkPhysicalDeviceProperties &getProperties() const;
 	void getProperties(VkPhysicalDeviceIDProperties *properties) const;
diff --git a/src/Vulkan/VkQueue.cpp b/src/Vulkan/VkQueue.cpp
index fc329e9..a0d143d 100644
--- a/src/Vulkan/VkQueue.cpp
+++ b/src/Vulkan/VkQueue.cpp
@@ -32,10 +32,14 @@
 
 namespace vk {
 
-Queue::Queue(Device *device, marl::Scheduler *scheduler)
+Queue::Queue(Device *device, marl::Scheduler *scheduler, VkDeviceQueueCreateFlags flags)
     : device(device)
 {
 	queueThread = std::thread(&Queue::taskLoop, this, scheduler);
+	if (((int)flags & VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR) != 0)
+	{
+		internalMutex.reset(new marl::mutex);
+	}
 }
 
 Queue::~Queue()
@@ -256,4 +260,13 @@
 	// Optional single debug label
 }
 
+std::unique_lock<marl::mutex> Queue::getInternalLock()
+{
+	if (internalMutex)
+	{
+		return std::unique_lock<marl::mutex>(*internalMutex);
+	}
+	return std::unique_lock<marl::mutex>();
+}
+
 }  // namespace vk
diff --git a/src/Vulkan/VkQueue.hpp b/src/Vulkan/VkQueue.hpp
index 5691020..1b145ef 100644
--- a/src/Vulkan/VkQueue.hpp
+++ b/src/Vulkan/VkQueue.hpp
@@ -43,7 +43,7 @@
 	VK_LOADER_DATA loaderData = { ICD_LOADER_MAGIC };
 
 public:
-	Queue(Device *device, marl::Scheduler *scheduler);
+	Queue(Device *device, marl::Scheduler *scheduler, VkDeviceQueueCreateFlags flags);
 	~Queue();
 
 	operator VkQueue()
@@ -61,6 +61,8 @@
 	void endDebugUtilsLabel();
 	void insertDebugUtilsLabel(const VkDebugUtilsLabelEXT *pLabelInfo);
 
+	std::unique_lock<marl::mutex> getInternalLock();
+
 private:
 	struct Task
 	{
@@ -85,6 +87,8 @@
 	sw::Chan<Task> pending;
 	sw::Chan<SubmitInfo *> toDelete;
 	std::thread queueThread;
+	// For VK_KHR_internally_synchronized_queues
+	std::unique_ptr<marl::mutex> internalMutex;
 };
 
 static inline Queue *Cast(VkQueue object)
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index d6bc854..6e4dad0 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -459,6 +459,7 @@
 	{ { VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION } },
 	{ { VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, VK_KHR_PIPELINE_LIBRARY_SPEC_VERSION } },
 	{ { VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME, VK_KHR_UNIFIED_IMAGE_LAYOUTS_SPEC_VERSION } },
+	{ { VK_KHR_INTERNALLY_SYNCHRONIZED_QUEUES_EXTENSION_NAME, VK_KHR_INTERNALLY_SYNCHRONIZED_QUEUES_SPEC_VERSION } },
 #ifndef __ANDROID__
 	{ { VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION } },
 	{ { VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, VK_KHR_SWAPCHAIN_MAINTENANCE_1_SPEC_VERSION } },
@@ -1243,6 +1244,7 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES:
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INTERNALLY_SYNCHRONIZED_QUEUES_FEATURES_KHR:
 			break;
 		default:
 			// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
@@ -1268,7 +1270,8 @@
 	for(uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++)
 	{
 		const VkDeviceQueueCreateInfo &queueCreateInfo = pCreateInfo->pQueueCreateInfos[i];
-		if(queueCreateInfo.flags != 0)
+
+		if((queueCreateInfo.flags & ~VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR) != 0)
 		{
 			UNSUPPORTED("pCreateInfo->pQueueCreateInfos[%d]->flags 0x%08X", i, queueCreateInfo.flags);
 		}
@@ -1388,6 +1391,7 @@
 	TRACE("(VkQueue queue = %p, uint32_t submitCount = %d, const VkSubmitInfo* pSubmits = %p, VkFence fence = %p)",
 	      queue, submitCount, pSubmits, static_cast<void *>(fence));
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	return vk::Cast(queue)->submit(submitCount, vk::SubmitInfo::Allocate(submitCount, pSubmits), vk::Cast(fence));
 }
 
@@ -1396,6 +1400,7 @@
 	TRACE("(VkQueue queue = %p, uint32_t submitCount = %d, const VkSubmitInfo2* pSubmits = %p, VkFence fence = %p)",
 	      queue, submitCount, pSubmits, static_cast<void *>(fence));
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	return vk::Cast(queue)->submit(submitCount, vk::SubmitInfo::Allocate(submitCount, pSubmits), vk::Cast(fence));
 }
 
@@ -1403,6 +1408,7 @@
 {
 	TRACE("(VkQueue queue = %p)", queue);
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	return vk::Cast(queue)->waitIdle();
 }
 
@@ -1679,6 +1685,7 @@
 {
 	TRACE("()");
 	UNSUPPORTED("vkQueueBindSparse");
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	return VK_SUCCESS;
 }
 
@@ -4214,7 +4221,7 @@
 		extInfo = extInfo->pNext;
 	}
 
-	if(pQueueInfo->flags != 0)
+	if((pQueueInfo->flags & VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT) != 0)
 	{
 		// The only flag that can be set here is VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT
 		// According to the Vulkan 1.2.132 spec, 4.3.1. Queue Family Properties:
@@ -4488,6 +4495,7 @@
 	TRACE("(VkQueue queue = %p, const VkDebugUtilsLabelEXT* pLabelInfo = %p)",
 	      queue, pLabelInfo);
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	vk::Cast(queue)->beginDebugUtilsLabel(pLabelInfo);
 }
 
@@ -4495,6 +4503,7 @@
 {
 	TRACE("(VkQueue queue = %p)", queue);
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	vk::Cast(queue)->endDebugUtilsLabel();
 }
 
@@ -4503,6 +4512,7 @@
 	TRACE("(VkQueue queue = %p, const VkDebugUtilsLabelEXT* pLabelInfo = %p)",
 	      queue, pLabelInfo);
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	vk::Cast(queue)->insertDebugUtilsLabel(pLabelInfo);
 }
 
@@ -4922,6 +4932,7 @@
 	TRACE("(VkQueue queue = %p, const VkPresentInfoKHR* pPresentInfo = %p)",
 	      queue, pPresentInfo);
 
+	auto queueLock = vk::Cast(queue)->getInternalLock();
 	return vk::Cast(queue)->present(pPresentInfo);
 }