Validate shared semaphore and memory handles

Checks if we support specific handles and specific usages on the
current device. If we don't, Supported() fails and we stop the import.

Bug: chromium:976495
Change-Id: Icfe044a3c4d912913823728100888ab05f22afd5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/10160
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Idan Raiter <idanr@google.com>
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 354809f..128cfdd 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -580,11 +580,19 @@
                                            VkSemaphore* outSignalSemaphore,
                                            VkDeviceMemory* outAllocation,
                                            std::vector<VkSemaphore>* outWaitSemaphores) {
+        const TextureDescriptor* textureDescriptor =
+            reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
+
         // Check services support this combination of handle type / image info
         if (!mExternalSemaphoreService->Supported()) {
             return DAWN_VALIDATION_ERROR("External semaphore usage not supported");
         }
-        if (!mExternalMemoryService->Supported()) {
+        if (!mExternalMemoryService->Supported(
+                VulkanImageFormat(textureDescriptor->format), VK_IMAGE_TYPE_2D,
+                VK_IMAGE_TILING_OPTIMAL,
+                VulkanImageUsage(textureDescriptor->usage,
+                                 GetValidInternalFormat(textureDescriptor->format)),
+                VK_IMAGE_CREATE_ALIAS_BIT_KHR)) {
             return DAWN_VALIDATION_ERROR("External memory usage not supported");
         }
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryService.h b/src/dawn_native/vulkan/external_memory/MemoryService.h
index 8d27db6..e49d3ff 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryService.h
+++ b/src/dawn_native/vulkan/external_memory/MemoryService.h
@@ -31,7 +31,11 @@
         ~Service();
 
         // True if the device reports it supports this feature
-        bool Supported();
+        bool Supported(VkFormat format,
+                       VkImageType type,
+                       VkImageTiling tiling,
+                       VkImageUsageFlags usage,
+                       VkImageCreateFlags flags);
 
         // Given an external handle pointing to memory, import it into a VkDeviceMemory
         ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
@@ -42,7 +46,7 @@
         Device* mDevice = nullptr;
 
         // True if early checks pass that determine if the service is supported
-        bool mSupportedFirstPass = false;
+        bool mSupported = false;
     };
 
 }}}  // namespace dawn_native::vulkan::external_memory
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
index d10ddc7..7804ad3 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp
@@ -19,12 +19,16 @@
 
     Service::Service(Device* device) : mDevice(device) {
         DAWN_UNUSED(mDevice);
-        DAWN_UNUSED(mSupportedFirstPass);
+        DAWN_UNUSED(mSupported);
     }
 
     Service::~Service() = default;
 
-    bool Service::Supported() {
+    bool Service::Supported(VkFormat format,
+                            VkImageType type,
+                            VkImageTiling tiling,
+                            VkImageUsageFlags usage,
+                            VkImageCreateFlags flags) {
         return false;
     }
 
diff --git a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
index e1f38db..d6e0e5a 100644
--- a/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "dawn_native/vulkan/AdapterVk.h"
+#include "dawn_native/vulkan/BackendVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/VulkanError.h"
 #include "dawn_native/vulkan/external_memory/MemoryService.h"
@@ -19,17 +21,62 @@
 namespace dawn_native { namespace vulkan { namespace external_memory {
 
     Service::Service(Device* device) : mDevice(device) {
-        const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
-        mSupportedFirstPass = info.externalMemory && info.externalMemoryFD;
+        const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo();
+        const VulkanGlobalInfo& globalInfo =
+            ToBackend(mDevice->GetAdapter())->GetBackend()->GetGlobalInfo();
+
+        mSupported = globalInfo.getPhysicalDeviceProperties2 &&
+                     globalInfo.externalMemoryCapabilities && deviceInfo.externalMemory &&
+                     deviceInfo.externalMemoryFD;
     }
 
     Service::~Service() = default;
 
-    bool Service::Supported() {
-        // TODO(idanr): Query device here for additional support information, decide if supported
-        // This will likely be done through vkGetPhysicalDeviceImageFormatProperties2KHR, where
-        // we give it the intended image properties and handle type and see if it's supported
-        return mSupportedFirstPass;
+    bool Service::Supported(VkFormat format,
+                            VkImageType type,
+                            VkImageTiling tiling,
+                            VkImageUsageFlags usage,
+                            VkImageCreateFlags flags) {
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return false;
+        }
+
+        VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo;
+        externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR;
+        externalFormatInfo.pNext = nullptr;
+        externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+        VkPhysicalDeviceImageFormatInfo2 formatInfo;
+        formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR;
+        formatInfo.pNext = &externalFormatInfo;
+        formatInfo.format = format;
+        formatInfo.type = type;
+        formatInfo.tiling = tiling;
+        formatInfo.usage = usage;
+        formatInfo.flags = flags;
+
+        VkExternalImageFormatProperties externalFormatProperties;
+        externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR;
+        externalFormatProperties.pNext = nullptr;
+
+        VkImageFormatProperties2 formatProperties;
+        formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+        formatProperties.pNext = &externalFormatProperties;
+
+        VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2KHR(
+            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties);
+
+        // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        // TODO(http://crbug.com/dawn/206): Investigate dedicated only images
+        VkFlags memoryFlags =
+            externalFormatProperties.externalMemoryProperties.externalMemoryFeatures;
+        return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) &&
+               !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR);
     }
 
     ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h b/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
index bfa72c4..cceaa2d 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h
@@ -46,7 +46,7 @@
         Device* mDevice = nullptr;
 
         // True if early checks pass that determine if the service is supported
-        bool mSupportedFirstPass = false;
+        bool mSupported = false;
     };
 
 }}}  // namespace dawn_native::vulkan::external_semaphore
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
index e7fd859..aca4cb1 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp
@@ -19,7 +19,7 @@
 
     Service::Service(Device* device) : mDevice(device) {
         DAWN_UNUSED(mDevice);
-        DAWN_UNUSED(mSupportedFirstPass);
+        DAWN_UNUSED(mSupported);
     }
 
     Service::~Service() = default;
diff --git a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
index 7312142..ea7bf47 100644
--- a/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
+++ b/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "dawn_native/vulkan/AdapterVk.h"
+#include "dawn_native/vulkan/BackendVk.h"
 #include "dawn_native/vulkan/DeviceVk.h"
 #include "dawn_native/vulkan/VulkanError.h"
 #include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
@@ -19,17 +21,43 @@
 namespace dawn_native { namespace vulkan { namespace external_semaphore {
 
     Service::Service(Device* device) : mDevice(device) {
-        const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
-        mSupportedFirstPass = info.externalSemaphore && info.externalSemaphoreFD;
-        // TODO(idanr): Query device here for additional support information, decide if supported
-        // This will likely be done through vkGetPhysicalDeviceExternalSemaphorePropertiesKHR, where
-        // we give it the intended handle type and see if it's supported
+        const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo();
+        const VulkanGlobalInfo& globalInfo =
+            ToBackend(mDevice->GetAdapter())->GetBackend()->GetGlobalInfo();
+
+        mSupported = globalInfo.getPhysicalDeviceProperties2 &&
+                     globalInfo.externalSemaphoreCapabilities && deviceInfo.externalSemaphore &&
+                     deviceInfo.externalSemaphoreFD;
+
+        // Early out before we try using extension functions
+        if (!mSupported) {
+            return;
+        }
+
+        VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo;
+        semaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR;
+        semaphoreInfo.pNext = nullptr;
+        semaphoreInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
+
+        VkExternalSemaphorePropertiesKHR semaphoreProperties;
+        semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR;
+        semaphoreProperties.pNext = nullptr;
+
+        mDevice->fn.GetPhysicalDeviceExternalSemaphorePropertiesKHR(
+            ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &semaphoreInfo,
+            &semaphoreProperties);
+
+        VkFlags requiredFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR |
+                                VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR;
+        mSupported =
+            mSupported &&
+            ((semaphoreProperties.externalSemaphoreFeatures & requiredFlags) == requiredFlags);
     }
 
     Service::~Service() = default;
 
     bool Service::Supported() {
-        return mSupportedFirstPass;
+        return mSupported;
     }
 
     ResultOrError<VkSemaphore> Service::ImportSemaphore(ExternalSemaphoreHandle handle) {