[vulkan] Implement VK_KHR_external_memory_fd for Linux and Android.

This extension allows one to import/export device memory buffers
through shared memory region file descriptors.

This also adds checks to ensure that binding a buffer or image
to an external device memory works only if VkCreate{Buffer,Image}
was called with a VkExternalMemory{Buffer,Image}CreateInfo struct
with compatible handle types.

Test: dEQP-VK.api.external.memory.opaque_fd*

Bug: b/140419396
Change-Id: I5d249685896ae0764bc9d5c635cc3799323db453
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35152
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: David Turner <digit@google.com>
diff --git a/src/Vulkan/BUILD.gn b/src/Vulkan/BUILD.gn
index 5888f34..bd7d521 100644
--- a/src/Vulkan/BUILD.gn
+++ b/src/Vulkan/BUILD.gn
@@ -91,6 +91,7 @@
   ]
   if (is_linux || is_android) {
     sources += [
+      "VkDeviceMemoryExternalLinux.hpp",
       "VkSemaphoreExternalLinux.hpp",
     ]
   } else if (is_fuchsia) {
diff --git a/src/Vulkan/VkBuffer.cpp b/src/Vulkan/VkBuffer.cpp
index 7c09a42..f8d1211 100644
--- a/src/Vulkan/VkBuffer.cpp
+++ b/src/Vulkan/VkBuffer.cpp
@@ -31,6 +31,16 @@
 		queueFamilyIndices = reinterpret_cast<uint32_t*>(mem);
 		memcpy(queueFamilyIndices, pCreateInfo->pQueueFamilyIndices, sizeof(uint32_t) * queueFamilyIndexCount);
 	}
+
+	const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+	for (; nextInfo != nullptr; nextInfo = nextInfo->pNext)
+	{
+		if (nextInfo->sType == VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO)
+		{
+			const auto* externalInfo = reinterpret_cast<const VkExternalMemoryBufferCreateInfo*>(nextInfo);
+			supportedExternalMemoryHandleTypes = externalInfo->handleTypes;
+		}
+	}
 }
 
 void Buffer::destroy(const VkAllocationCallbacks* pAllocator)
@@ -68,6 +78,11 @@
 	return memoryRequirements;
 }
 
+bool Buffer::canBindToMemory(DeviceMemory* pDeviceMemory) const
+{
+	return pDeviceMemory->checkExternalMemoryHandleType(supportedExternalMemoryHandleTypes);
+}
+
 void Buffer::bind(DeviceMemory* pDeviceMemory, VkDeviceSize pMemoryOffset)
 {
 	memory = pDeviceMemory->getOffsetPointer(pMemoryOffset);
diff --git a/src/Vulkan/VkBuffer.hpp b/src/Vulkan/VkBuffer.hpp
index 286e0ed..23c42e1 100644
--- a/src/Vulkan/VkBuffer.hpp
+++ b/src/Vulkan/VkBuffer.hpp
@@ -40,6 +40,7 @@
 	void* getOffsetPointer(VkDeviceSize offset) const;
 	inline VkDeviceSize getSize() const { return size; }
 	uint8_t* end() const;
+	bool canBindToMemory(DeviceMemory* pDeviceMemory) const;
 
 private:
 	void*                 memory = nullptr;
@@ -49,6 +50,8 @@
 	VkSharingMode         sharingMode = VK_SHARING_MODE_EXCLUSIVE;
 	uint32_t              queueFamilyIndexCount = 0;
 	uint32_t*             queueFamilyIndices = nullptr;
+
+	VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleTypes = (VkExternalMemoryHandleTypeFlags)0;
 };
 
 static inline Buffer* Cast(VkBuffer object)
diff --git a/src/Vulkan/VkConfig.h b/src/Vulkan/VkConfig.h
index ddb8d89..5481d7d 100644
--- a/src/Vulkan/VkConfig.h
+++ b/src/Vulkan/VkConfig.h
@@ -85,6 +85,7 @@
 }
 
 #if VK_USE_PLATFORM_XLIB_KHR || VK_USE_PLATFORM_ANDROID_KHR
+#define SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD        1
 #define SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD     1
 #endif
 
diff --git a/src/Vulkan/VkDeviceMemory.cpp b/src/Vulkan/VkDeviceMemory.cpp
index 3e9a952..ee56500 100644
--- a/src/Vulkan/VkDeviceMemory.cpp
+++ b/src/Vulkan/VkDeviceMemory.cpp
@@ -19,36 +19,164 @@
 namespace vk
 {
 
-DeviceMemory::DeviceMemory(const VkMemoryAllocateInfo* pCreateInfo, void* mem) :
-	size(pCreateInfo->allocationSize), memoryTypeIndex(pCreateInfo->memoryTypeIndex)
+// Base abstract interface for a device memory implementation.
+class DeviceMemory::ExternalBase
+{
+public:
+	virtual ~ExternalBase() = default;
+
+    // Allocate the memory according to |size|. On success return VK_SUCCESS
+    // and sets |*pBuffer|.
+	virtual VkResult allocate(size_t size, void** pBuffer) = 0;
+
+    // Deallocate previously allocated memory at |buffer|.
+	virtual void deallocate(void* buffer, size_t size) = 0;
+
+    // Return the handle type flag bit supported by this implementation.
+    // A value of 0 corresponds to non-external memory.
+	virtual VkExternalMemoryHandleTypeFlagBits getFlagBit() const = 0;
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	virtual VkResult exportFd(int* pFd) const
+	{
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
+#endif
+
+protected:
+	ExternalBase() = default;
+};
+
+// Small class describing a given DeviceMemory::ExternalBase derived class.
+// |typeFlagBit| corresponds to the external memory handle type.
+// |instanceSize| is the size of each class instance in bytes.
+// |instanceInit| is a function pointer used to initialize an instance inplace
+// according to a |pAllocateInfo| parameter.
+class ExternalMemoryTraits
+{
+public:
+	VkExternalMemoryHandleTypeFlagBits typeFlagBit;
+	size_t instanceSize;
+	void (*instanceInit)(void* external, const VkMemoryAllocateInfo* pAllocateInfo);
+};
+
+// Template function that parses a |pAllocateInfo.pNext| chain to verify that
+// it asks for the creation or import of a memory type managed by implementation
+// class T. On success, return true and sets |pTraits| accordingly. Otherwise
+// return false.
+template <typename  T>
+static bool parseCreateInfo(const VkMemoryAllocateInfo* pAllocateInfo,
+							ExternalMemoryTraits* pTraits)
+{
+	if (T::supportsAllocateInfo(pAllocateInfo))
+	{
+		pTraits->typeFlagBit = T::typeFlagBit;
+		pTraits->instanceSize = sizeof(T);
+		pTraits->instanceInit = [](void* external,
+								   const VkMemoryAllocateInfo* pAllocateInfo) {
+			new (external) T(pAllocateInfo);
+		};
+		return true;
+	}
+	return false;
+}
+
+// DeviceMemory::ExternalBase implementation that uses host memory.
+// Not really external, but makes everything simpler.
+class DeviceMemoryHostExternalBase : public DeviceMemory::ExternalBase
+{
+public:
+
+    // Does not support any external memory type at all.
+	static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = (VkExternalMemoryHandleTypeFlagBits)0;
+
+    // Always return true as is used as a fallback in findTraits() below.
+	static bool supportsAllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+	{
+		return true;
+	}
+
+	DeviceMemoryHostExternalBase(const VkMemoryAllocateInfo* pAllocateInfo) {}
+
+	VkResult allocate(size_t size, void** pBuffer) override
+	{
+		void* buffer = vk::allocate(size, REQUIRED_MEMORY_ALIGNMENT, DEVICE_MEMORY);
+		if (!buffer)
+			return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+
+		*pBuffer = buffer;
+		return VK_SUCCESS;
+	}
+
+	void deallocate(void* buffer, size_t size) override
+	{
+		vk::deallocate(buffer, DEVICE_MEMORY);
+	}
+
+	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
+	{
+		return typeFlagBit;
+	}
+};
+
+}  // namespace vk
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+#include "VkDeviceMemoryExternalLinux.hpp"
+#endif
+
+namespace vk
+{
+
+static void findTraits(const VkMemoryAllocateInfo* pAllocateInfo,
+					   ExternalMemoryTraits*       pTraits)
+{
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	if (parseCreateInfo<LinuxMemfdExternalMemory>(pAllocateInfo, pTraits))
+	{
+		return;
+	}
+#endif
+	parseCreateInfo<DeviceMemoryHostExternalBase>(pAllocateInfo, pTraits);
+}
+
+DeviceMemory::DeviceMemory(const VkMemoryAllocateInfo* pAllocateInfo, void* mem) :
+	size(pAllocateInfo->allocationSize), memoryTypeIndex(pAllocateInfo->memoryTypeIndex),
+	external(reinterpret_cast<ExternalBase *>(mem))
 {
 	ASSERT(size);
+
+	ExternalMemoryTraits traits;
+	findTraits(pAllocateInfo, &traits);
+	traits.instanceInit(external, pAllocateInfo);
 }
 
 void DeviceMemory::destroy(const VkAllocationCallbacks* pAllocator)
 {
-	vk::deallocate(buffer, DEVICE_MEMORY);
+	if (buffer)
+	{
+		external->deallocate(buffer, size);
+		buffer = nullptr;
+	}
+	external->~ExternalBase();  // Call virtual destructor in place.
+	vk::deallocate(external, pAllocator);
 }
 
-size_t DeviceMemory::ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pCreateInfo)
+size_t DeviceMemory::ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pAllocateInfo)
 {
-	// buffer is "GPU memory", so we use device memory for it
-	return 0;
+	ExternalMemoryTraits traits;
+	findTraits(pAllocateInfo, &traits);
+	return traits.instanceSize;
 }
 
 VkResult DeviceMemory::allocate()
 {
-	if(!buffer)
+	VkResult result = VK_SUCCESS;
+	if (!buffer)
 	{
-		buffer = vk::allocate(size, REQUIRED_MEMORY_ALIGNMENT, DEVICE_MEMORY);
+		result = external->allocate(size, &buffer);
 	}
-
-	if(!buffer)
-	{
-		return VK_ERROR_OUT_OF_DEVICE_MEMORY;
-	}
-
-	return VK_SUCCESS;
+	return result;
 }
 
 VkResult DeviceMemory::map(VkDeviceSize pOffset, VkDeviceSize pSize, void** ppData)
@@ -70,4 +198,33 @@
 	return reinterpret_cast<char*>(buffer) + pOffset;
 }
 
+bool DeviceMemory::checkExternalMemoryHandleType(
+		VkExternalMemoryHandleTypeFlags supportedHandleTypes) const
+{
+	if (!supportedHandleTypes)
+	{
+		// This image or buffer does not need to be stored on external
+		// memory, so this check should always pass.
+		return true;
+	}
+	VkExternalMemoryHandleTypeFlagBits handle_type_bit = external->getFlagBit();
+	if (!handle_type_bit)
+	{
+		// This device memory is not external and can accomodate
+		// any image or buffer as well.
+		return true;
+	}
+	// Return true only if the external memory type is compatible with the
+	// one specified during VkCreate{Image,Buffer}(), through a
+	// VkExternalMemory{Image,Buffer}AllocateInfo struct.
+	return (supportedHandleTypes & handle_type_bit) != 0;
+}
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+VkResult DeviceMemory::exportFd(int* pFd) const
+{
+	return external->exportFd(pFd);
+}
+#endif
+
 } // namespace vk
diff --git a/src/Vulkan/VkDeviceMemory.hpp b/src/Vulkan/VkDeviceMemory.hpp
index f4eb05f..e63cb7e 100644
--- a/src/Vulkan/VkDeviceMemory.hpp
+++ b/src/Vulkan/VkDeviceMemory.hpp
@@ -15,6 +15,7 @@
 #ifndef VK_DEVICE_MEMORY_HPP_
 #define VK_DEVICE_MEMORY_HPP_
 
+#include "VkConfig.h"
 #include "VkObject.hpp"
 
 namespace vk
@@ -27,6 +28,10 @@
 
 	static size_t ComputeRequiredAllocationSize(const VkMemoryAllocateInfo* pCreateInfo);
 
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	VkResult exportFd(int* pFd) const;
+#endif
+
 	void destroy(const VkAllocationCallbacks* pAllocator);
 	VkResult allocate();
 	VkResult map(VkDeviceSize offset, VkDeviceSize size, void** ppData);
@@ -34,10 +39,20 @@
 	void* getOffsetPointer(VkDeviceSize pOffset) const;
 	uint32_t getMemoryTypeIndex() const { return memoryTypeIndex; }
 
+	// If this is external memory, return true iff its handle type matches the bitmask
+	// provided by |supportedExternalHandleTypes|. Otherwise, always return true.
+	bool checkExternalMemoryHandleType(
+				VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleType) const;
+
+	// Internal implementation class for external memory. Platform-specific.
+	class ExternalBase;
+
 private:
-	void*        buffer = nullptr;
-	VkDeviceSize size = 0;
-	uint32_t     memoryTypeIndex = 0;
+
+	void*         buffer = nullptr;
+	VkDeviceSize  size = 0;
+	uint32_t      memoryTypeIndex = 0;
+	ExternalBase* external = nullptr;
 };
 
 static inline DeviceMemory* Cast(VkDeviceMemory object)
diff --git a/src/Vulkan/VkDeviceMemoryExternalLinux.hpp b/src/Vulkan/VkDeviceMemoryExternalLinux.hpp
new file mode 100644
index 0000000..6c4fb5f
--- /dev/null
+++ b/src/Vulkan/VkDeviceMemoryExternalLinux.hpp
@@ -0,0 +1,149 @@
+// Copyright 2019 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 "VkDebug.hpp"
+#include "System/Linux/MemFd.hpp"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+
+class LinuxMemfdExternalMemory : public vk::DeviceMemory::ExternalBase
+{
+public:
+	// Helper struct to parse the VkMemoryAllocateInfo.pNext chain and
+	// extract relevant information related to the handle type supported
+	// by this DeviceMemory;:ExternalBase subclass.
+	struct AllocateInfo
+	{
+		bool importFd = false;
+		bool exportFd = false;
+		int fd = -1;
+
+		AllocateInfo() = default;
+
+		// Parse the VkMemoryAllocateInfo.pNext chain to initialize an AllocateInfo.
+		AllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+		{
+			const auto* createInfo = reinterpret_cast<const VkBaseInStructure*>(pAllocateInfo->pNext);
+			while (createInfo)
+			{
+				switch (createInfo->sType)
+				{
+				case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
+					{
+						const auto* importInfo = reinterpret_cast<const VkImportMemoryFdInfoKHR*>(createInfo);
+
+						if (importInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+						{
+							UNIMPLEMENTED("importInfo->handleType");
+						}
+						importFd = true;
+						fd = importInfo->fd;
+					}
+					break;
+				case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
+					{
+						const auto* exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo*>(createInfo);
+
+						if (exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+						{
+							UNIMPLEMENTED("exportInfo->handleTypes");
+						}
+						exportFd = true;
+					}
+					break;
+
+				default:
+					;
+				}
+				createInfo = createInfo->pNext;
+			}
+		}
+	};
+
+	static const VkExternalMemoryHandleTypeFlagBits typeFlagBit = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+
+	static bool supportsAllocateInfo(const VkMemoryAllocateInfo* pAllocateInfo)
+	{
+		AllocateInfo info(pAllocateInfo);
+		return info.importFd || info.exportFd;
+	}
+
+	explicit LinuxMemfdExternalMemory(const VkMemoryAllocateInfo* pAllocateInfo)
+			: allocateInfo(pAllocateInfo)
+	{
+	}
+
+	~LinuxMemfdExternalMemory()
+	{
+		memfd.close();
+	}
+
+	VkResult allocate(size_t size, void** pBuffer) override
+	{
+		if (allocateInfo.importFd)
+		{
+			memfd.importFd(allocateInfo.fd);
+			if (!memfd.isValid())
+			{
+				return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+			}
+		}
+		else
+		{
+			ASSERT(allocateInfo.exportFd);
+			static int counter = 0;
+			char name[40];
+			snprintf(name, sizeof(name), "SwiftShader.Memory.%d", ++counter);
+			if (!memfd.allocate(name, size))
+			{
+				TRACE("memfd.allocate() returned %s", strerror(errno));
+				return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+			}
+		}
+		void* addr = memfd.mapReadWrite(0, size);
+		if (!addr)
+		{
+			return VK_ERROR_MEMORY_MAP_FAILED;
+		}
+		*pBuffer = addr;
+		return VK_SUCCESS;
+	}
+
+	void deallocate(void* buffer, size_t size) override
+	{
+		memfd.unmap(buffer, size);
+	}
+
+	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
+	{
+		return typeFlagBit;
+	}
+
+	VkResult exportFd(int* pFd) const override
+	{
+		int fd = memfd.exportFd();
+		if (fd < 0)
+		{
+			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+		}
+		*pFd = fd;
+		return VK_SUCCESS;
+	}
+
+private:
+	LinuxMemFd   memfd;
+	AllocateInfo allocateInfo;
+};
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 636995b..e17aa75 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -361,6 +361,17 @@
 		}
 	},
 #endif
+
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	// VK_KHR_external_memory_fd
+	{
+		VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
+		{
+			MAKE_VULKAN_DEVICE_ENTRY(vkGetMemoryFdKHR),
+			MAKE_VULKAN_DEVICE_ENTRY(vkGetMemoryFdPropertiesKHR),
+		}
+	},
+#endif
 };
 
 #undef MAKE_VULKAN_DEVICE_ENTRY
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index bda8677..7379839 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -75,6 +75,16 @@
 		compressedImageCreateInfo.format = format.getDecompressedFormat();
 		decompressedImage = new (mem) Image(&compressedImageCreateInfo, nullptr, device);
 	}
+
+	const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+	for (; nextInfo != nullptr; nextInfo = nextInfo->pNext)
+	{
+		if (nextInfo->sType == VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO)
+		{
+			const auto* externalInfo = reinterpret_cast<const VkExternalMemoryImageCreateInfo*>(nextInfo);
+			supportedExternalMemoryHandleTypes = externalInfo->handleTypes;
+		}
+	}
 }
 
 void Image::destroy(const VkAllocationCallbacks* pAllocator)
@@ -100,6 +110,11 @@
 	return memoryRequirements;
 }
 
+bool Image::canBindToMemory(DeviceMemory* pDeviceMemory) const
+{
+	return pDeviceMemory->checkExternalMemoryHandleType(supportedExternalMemoryHandleTypes);
+}
+
 void Image::bind(DeviceMemory* pDeviceMemory, VkDeviceSize pMemoryOffset)
 {
 	deviceMemory = pDeviceMemory;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 7d1cf7c..bed9752 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -81,6 +81,7 @@
 	bool                     is3DSlice() const;
 	uint8_t*                 end() const;
 	VkDeviceSize             getLayerSize(VkImageAspectFlagBits aspect) const;
+	bool                     canBindToMemory(DeviceMemory* pDeviceMemory) const;
 
 	void                     prepareForSampling(const VkImageSubresourceRange& subresourceRange);
 	const Image*             getSampledImage(const vk::Format& imageViewFormat) const;
@@ -125,6 +126,8 @@
 #ifdef __ANDROID__
 	BackingMemory            backingMemory = {};
 #endif
+
+	VkExternalMemoryHandleTypeFlags supportedExternalMemoryHandleTypes = (VkExternalMemoryHandleTypeFlags)0;
 };
 
 static inline Image* Cast(VkImage object)
@@ -134,4 +137,4 @@
 
 } // namespace vk
 
-#endif // VK_IMAGE_HPP_
\ No newline at end of file
+#endif // VK_IMAGE_HPP_
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index d61b413..0598a81 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -23,6 +23,22 @@
 namespace vk
 {
 
+static void setExternalMemoryProperties(VkExternalMemoryHandleTypeFlagBits handleType, VkExternalMemoryProperties* properties)
+{
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	if (handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+	{
+		properties->compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+		properties->exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+		properties->externalMemoryFeatures = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT;
+		return;
+	}
+#endif
+	properties->compatibleHandleTypes = 0;
+	properties->exportFromImportedHandleTypes = 0;
+	properties->externalMemoryFeatures = 0;
+}
+
 PhysicalDevice::PhysicalDevice(const void*, void* mem)
 {
 }
@@ -339,9 +355,7 @@
 
 void PhysicalDevice::getProperties(const VkExternalMemoryHandleTypeFlagBits* handleType, VkExternalImageFormatProperties* properties) const
 {
-	properties->externalMemoryProperties.compatibleHandleTypes = 0;
-	properties->externalMemoryProperties.exportFromImportedHandleTypes = 0;
-	properties->externalMemoryProperties.externalMemoryFeatures = 0;
+	setExternalMemoryProperties(*handleType, &properties->externalMemoryProperties);
 }
 
 void PhysicalDevice::getProperties(VkSamplerYcbcrConversionImageFormatProperties* properties) const
@@ -358,9 +372,7 @@
 
 void PhysicalDevice::getProperties(const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) const
 {
-	pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = 0;
-	pExternalBufferProperties->externalMemoryProperties.exportFromImportedHandleTypes = 0;
-	pExternalBufferProperties->externalMemoryProperties.externalMemoryFeatures = 0;
+	setExternalMemoryProperties(pExternalBufferInfo->handleType, &pExternalBufferProperties->externalMemoryProperties);
 }
 
 void PhysicalDevice::getProperties(const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) const
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index bee8173..16784ce 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -250,6 +250,9 @@
 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
 	{ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION },
 #endif
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+	{ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION },
+#endif
 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
 	{ VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION },
 #endif
@@ -796,8 +799,30 @@
 			// This extension controls on which physical devices the memory gets allocated.
 			// SwiftShader only has a single physical device, so this extension does nothing in this case.
 			break;
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+		case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
+		{
+			auto* importInfo = reinterpret_cast<const VkImportMemoryFdInfoKHR *>(allocationInfo);
+			if (importInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+			{
+				UNSUPPORTED("importInfo->handleType %u", importInfo->handleType);
+				return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+			}
+			break;
+		}
+		case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
+		{
+			auto* exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo *>(allocationInfo);
+			if (exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+			{
+				UNSUPPORTED("exportInfo->handleTypes %u", exportInfo->handleTypes);
+				return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+			}
+			break;
+		}
+#endif
 		default:
-			UNIMPLEMENTED("allocationInfo->sType");
+			UNIMPLEMENTED("allocationInfo->sType %u", allocationInfo->sType);
 			break;
 		}
 
@@ -829,6 +854,46 @@
 	vk::destroy(memory, pAllocator);
 }
 
+#if SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHR(VkDevice device, const VkMemoryGetFdInfoKHR* getFdInfo, int* pFd)
+{
+	TRACE("(VkDevice device = %p, const VkMemoryGetFdInfoKHR* getFdInfo = %p, int* pFd = %p",
+		  device, getFdInfo, pFd);
+
+	if (getFdInfo->handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+	{
+		UNSUPPORTED("pGetFdInfo->handleType %u", getFdInfo->handleType);
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
+	return vk::Cast(getFdInfo->memory)->exportFd(pFd);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHR(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, VkMemoryFdPropertiesKHR* pMemoryFdProperties)
+{
+	TRACE("(VkDevice device = %p, VkExternalMemoryHandleTypeFlagBits handleType = %x, int fd = %d, VkMemoryFdPropertiesKHR* pMemoryFdProperties = %p)",
+		  device, handleType, fd, pMemoryFdProperties);
+
+	if (handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT)
+	{
+		UNSUPPORTED("handleType %u", handleType);
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
+
+	if (fd < 0)
+	{
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
+
+	const VkPhysicalDeviceMemoryProperties& memoryProperties =
+			vk::Cast(device)->getPhysicalDevice()->getMemoryProperties();
+
+	// All SwiftShader memory types support this!
+	pMemoryFdProperties->memoryTypeBits = (1U << memoryProperties.memoryTypeCount) - 1U;
+
+	return VK_SUCCESS;
+}
+#endif  // SWIFTSHADER_EXTERNAL_MEMORY_LINUX_MEMFD
+
 VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
 {
 	TRACE("(VkDevice device = %p, VkDeviceMemory memory = %p, VkDeviceSize offset = %d, VkDeviceSize size = %d, VkMemoryMapFlags flags = %d, void** ppData = %p)",
@@ -886,8 +951,12 @@
 	TRACE("(VkDevice device = %p, VkBuffer buffer = %p, VkDeviceMemory memory = %p, VkDeviceSize memoryOffset = %d)",
 		    device, static_cast<void*>(buffer), static_cast<void*>(memory), int(memoryOffset));
 
+	if (!vk::Cast(buffer)->canBindToMemory(vk::Cast(memory)))
+	{
+		UNSUPPORTED("vkBindBufferMemory with invalid external memory");
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
 	vk::Cast(buffer)->bind(vk::Cast(memory), memoryOffset);
-
 	return VK_SUCCESS;
 }
 
@@ -896,8 +965,12 @@
 	TRACE("(VkDevice device = %p, VkImage image = %p, VkDeviceMemory memory = %p, VkDeviceSize memoryOffset = %d)",
 		    device, static_cast<void*>(image), static_cast<void*>(memory), int(memoryOffset));
 
+	if (!vk::Cast(image)->canBindToMemory(vk::Cast(memory)))
+	{
+		UNSUPPORTED("vkBindImageMemory with invalid external memory");
+		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+	}
 	vk::Cast(image)->bind(vk::Cast(memory), memoryOffset);
-
 	return VK_SUCCESS;
 }
 
@@ -1158,9 +1231,18 @@
 	TRACE("(VkDevice device = %p, const VkBufferCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkBuffer* pBuffer = %p)",
 		    device, pCreateInfo, pAllocator, pBuffer);
 
-	if(pCreateInfo->pNext)
+	auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+	while (nextInfo)
 	{
-		UNIMPLEMENTED("pCreateInfo->pNext");
+		switch (nextInfo->sType)
+		{
+		case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO:
+			// Do nothing. Should be handled by vk::Buffer::Create().
+			break;
+		default:
+			UNIMPLEMENTED("pCreateInfo->pNext sType=0x%X", nextInfo->sType);
+		}
+		nextInfo = nextInfo->pNext;
 	}
 
 	return vk::Buffer::Create(pAllocator, pCreateInfo, pBuffer);
@@ -1227,6 +1309,9 @@
 		}
 		break;
 #endif
+		case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO:
+			// Do nothing. Should be handled by vk::Image::Create()
+			break;
 		case VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR:
 			/* Do nothing. We don't actually need the swapchain handle yet; we'll do all the work in vkBindImageMemory2. */
 			break;
@@ -1279,8 +1364,6 @@
 	TRACE("(VkDevice device = %p, VkImage image = %p, const VkAllocationCallbacks* pAllocator = %p)",
 		    device, static_cast<void*>(image), pAllocator);
 
-	vk::destroy(image, pAllocator);
-
 #ifdef __ANDROID__
 	vk::Image* img = vk::Cast(image);
 	if(img && img->hasExternalMemory())
@@ -1288,6 +1371,8 @@
 		vk::destroy(img->getExternalMemory(), pAllocator);
 	}
 #endif
+
+	vk::destroy(image, pAllocator);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout)
@@ -2249,6 +2334,15 @@
 			UNIMPLEMENTED("pBindInfos[%d].pNext", i);
 		}
 
+		if (!vk::Cast(pBindInfos[i].buffer)->canBindToMemory(vk::Cast(pBindInfos[i].memory)))
+		{
+			UNSUPPORTED("vkBindBufferMemory2 with invalid external memory");
+			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
+		}
+	}
+
+	for (uint32_t i = 0; i < bindInfoCount; i++)
+	{
 		vk::Cast(pBindInfos[i].buffer)->bind(vk::Cast(pBindInfos[i].memory), pBindInfos[i].memoryOffset);
 	}
 
@@ -2262,6 +2356,15 @@
 
 	for(uint32_t i = 0; i < bindInfoCount; i++)
 	{
+		if (!vk::Cast(pBindInfos[i].image)->canBindToMemory(vk::Cast(pBindInfos[i].memory)))
+		{
+			UNSUPPORTED("vkBindImageMemory2 with invalid external memory");
+			return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+		}
+	}
+
+	for(uint32_t i = 0; i < bindInfoCount; i++)
+	{
 		vk::DeviceMemory *memory = vk::Cast(pBindInfos[i].memory);
 		VkDeviceSize offset = pBindInfos[i].memoryOffset;