Update SwiftShader VK AHB to support generic gralloc

... by only using the AHardwareBuffer_* functions which will
use the device's gralloc implementation.

Note: this change does not fix the 5 existing deqp failures
in the suballocated.* tests that are mentioned in b/169796031.

Note: ANGLE doesn't seem to have support for
VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM yet: b/171302758.

Bug: b/141698760
Bug: b/147316305
Test: launch_cvd && cts -m CtsDeqpTestCases
  dEQP-VK.api.external.memory.android_hardware_buffer.*
Change-Id: Ia551e78198c20d6c40ffa9448418f73829da1880
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/49248
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Jason Macnak <natsu@google.com>
Reviewed-by: Trevor Black <vantablack@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Android.bp b/src/Android.bp
index 026e23c..1f9f5fa 100644
--- a/src/Android.bp
+++ b/src/Android.bp
@@ -56,7 +56,6 @@
             shared_libs: [
                 "android.hardware.graphics.mapper@3.0",
                 "android.hardware.graphics.mapper@4.0",
-                "libdrm",
                 "libnativewindow",
                 "libhardware",
                 "libhidlbase",
@@ -591,7 +590,6 @@
             shared_libs: [
                 "android.hardware.graphics.mapper@3.0",
                 "android.hardware.graphics.mapper@4.0",
-                "libdrm",
                 "libnativewindow",
                 "libhardware",
                 "libhidlbase",
@@ -664,8 +662,6 @@
     include_dirs: [
         "external/swiftshader/third_party/SPIRV-Headers/include",
         "external/swiftshader/include",
-        "external/minigbm",
-        "external/libdrm"
     ],
 }
 
diff --git a/src/Vulkan/VkBuffer.hpp b/src/Vulkan/VkBuffer.hpp
index 2aa59fc..fd95fe2 100644
--- a/src/Vulkan/VkBuffer.hpp
+++ b/src/Vulkan/VkBuffer.hpp
@@ -41,6 +41,9 @@
 	uint8_t *end() const;
 	bool canBindToMemory(DeviceMemory *pDeviceMemory) const;
 
+	VkBufferUsageFlags getUsage() const { return usage; }
+	VkBufferCreateFlags getFlags() const { return flags; }
+
 private:
 	void *memory = nullptr;
 	VkBufferCreateFlags flags = 0;
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index cc0a359..4cdca03 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -146,10 +146,6 @@
 		debugger.server = vk::dbg::Server::create(debugger.context, atoi(port));
 	}
 #endif  // ENABLE_VK_DEBUGGER
-
-#if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
-	ahbAddressMap.reset(new AHBAddressMap());
-#endif
 }
 
 void Device::destroy(const VkAllocationCallbacks *pAllocator)
@@ -379,60 +375,4 @@
 	}
 }
 
-#if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
-Device::AHBAddressMap *Device::getAHBAddressMap() const
-{
-	return ahbAddressMap.get();
-}
-
-void *Device::AHBAddressMap::query(const uint32_t key)
-{
-	std::unique_lock<std::mutex> lock(addressMapMutex);
-	if(addressMap.find(key) == addressMap.end())
-		return nullptr;
-
-	return addressMap[key].address;
-}
-
-void Device::AHBAddressMap::add(const uint32_t key, void *value)
-{
-	std::unique_lock<std::mutex> lock(addressMapMutex);
-	auto it = addressMap.find(key);
-	if(it == addressMap.end())
-	{
-		MapValue mv;
-		mv.refCount = 1;
-		mv.address = value;
-		addressMap[key] = mv;
-	}
-	else
-	{
-		it->second.address = value;
-		it->second.refCount++;
-	}
-}
-
-int Device::AHBAddressMap::incrementReference(const uint32_t key)
-{
-	std::unique_lock<std::mutex> lock(addressMapMutex);
-	auto it = addressMap.find(key);
-	if(it == addressMap.end())
-		return -1;
-
-	it->second.refCount++;
-	return it->second.refCount;
-}
-
-int Device::AHBAddressMap::decrementReference(const uint32_t key)
-{
-	std::unique_lock<std::mutex> lock(addressMapMutex);
-	auto it = addressMap.find(key);
-	if(it == addressMap.end())
-		return -1;
-
-	it->second.refCount--;
-	return it->second.refCount;
-}
-#endif  // SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
-
 }  // namespace vk
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index 9fce601..7f67ffa 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -193,41 +193,6 @@
 		std::shared_ptr<vk::dbg::Server> server;
 	} debugger;
 #endif  // ENABLE_VK_DEBUGGER
-
-#if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
-public:
-	class AHBAddressMap
-	{
-	public:
-		AHBAddressMap() {}
-		~AHBAddressMap() {}
-
-		struct MapValue
-		{
-			MapValue()
-			    : refCount(0)
-			    , address(nullptr)
-			{
-			}
-			int refCount;
-			void *address;
-		};
-
-		void *query(const uint32_t key);
-		int incrementReference(const uint32_t key);
-		int decrementReference(const uint32_t key);
-		void add(const uint32_t key, void *value);
-
-	private:
-		std::map<uint32_t, MapValue> addressMap;
-		std::mutex addressMapMutex;
-	};
-
-	AHBAddressMap *getAHBAddressMap() const;
-
-private:
-	std::unique_ptr<AHBAddressMap> ahbAddressMap;
-#endif
 };
 
 using DispatchableDevice = DispatchableObject<Device, VkDevice>;
diff --git a/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp b/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
index 8890829..80e3250 100644
--- a/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
+++ b/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
@@ -16,159 +16,19 @@
 
 #	include "VkDeviceMemoryExternalAndroid.hpp"
 
+#	include "System/Debug.hpp"
 #	include "VkDestroy.hpp"
-#	include "VkDevice.hpp"
 #	include "VkFormat.hpp"
 #	include "VkObject.hpp"
 #	include "VkPhysicalDevice.hpp"
-
-#	include "System/Debug.hpp"
-#	include "System/Linux/MemFd.hpp"
-#	include <sys/mman.h>
+#	include "VkStringify.hpp"
 
 #	include <android/hardware_buffer.h>
-#	include <cutils/native_handle.h>
 #	include <vndk/hardware_buffer.h>
 
-#	include <cros_gralloc/cros_gralloc_handle.h>
-#	include <external/virgl_hw.h>
-#	include <unistd.h>
-#	include <virtgpu_drm.h>
-#	include <xf86drm.h>
+namespace {
 
-AHardwareBufferExternalMemory::~AHardwareBufferExternalMemory()
-{
-	// correct deallocation of AHB does not require a pointer or size
-	deallocate(nullptr, 0);
-}
-
-VkResult AHardwareBufferExternalMemory::allocate(size_t size, void **pBuffer)
-{
-	if(allocateInfo.importAhb)
-	{
-		ahb = allocateInfo.ahb;
-		AHardwareBuffer_acquire(ahb);
-		return allocateAndroidHardwareBuffer(size, pBuffer);
-	}
-	else
-	{
-		ASSERT(allocateInfo.exportAhb);
-
-		// Outline ahbDesc
-		AHardwareBuffer_Desc ahbDesc;
-		if(allocateInfo.imageHandle)
-		{
-			ahbDesc.format = GetAndroidHardwareBufferDescFormat(VkFormat(allocateInfo.imageHandle->getFormat()));
-			VkExtent3D extent = allocateInfo.imageHandle->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			ahbDesc.width = extent.width;
-			ahbDesc.height = extent.height;
-			ahbDesc.layers = allocateInfo.imageHandle->getArrayLayers();
-			ahbDesc.stride = allocateInfo.imageHandle->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-
-			VkImageCreateFlags createFlags = allocateInfo.imageHandle->getFlags();
-			VkImageUsageFlags usageFlags = allocateInfo.imageHandle->getUsage();
-			GetAndroidHardwareBufferUsageFromVkUsage(createFlags, usageFlags, ahbDesc.usage);
-		}
-		else
-		{
-			ASSERT(allocateInfo.bufferHandle);
-			ahbDesc.format = AHARDWAREBUFFER_FORMAT_BLOB;
-			ahbDesc.width = uint32_t(allocateInfo.bufferHandle->getSize());
-			ahbDesc.height = 1;
-			ahbDesc.layers = 1;
-			ahbDesc.stride = uint32_t(allocateInfo.bufferHandle->getSize());
-			// TODO(b/141698760)
-			//   This will be fairly specific, needs fleshing out
-			ahbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
-		}
-
-		// create a new ahb from desc
-		if(AHardwareBuffer_allocate(&ahbDesc, &ahb) != 0)
-		{
-			return VK_ERROR_OUT_OF_HOST_MEMORY;
-		}
-
-		return allocateAndroidHardwareBuffer(size, pBuffer);
-	}
-}
-
-VkResult AHardwareBufferExternalMemory::allocateAndroidHardwareBuffer(size_t size, void **pBuffer)
-{
-	// get native_handle_t from ahb
-	const native_handle_t *h = AHardwareBuffer_getNativeHandle(ahb);
-	if(h == nullptr)
-	{
-		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-	}
-
-	// get rendernodeFD and primeHandle from native_handle_t.data
-	uint32_t primeHandle;
-	createRenderNodeFD();
-	VkResult result = getPrimeHandle(h, primeHandle);
-	if(result != VK_SUCCESS)
-	{
-		return result;
-	}
-
-	// get memory pointer from store or mmap it
-	vk::Device::AHBAddressMap *pDeviceHandleMap = device->getAHBAddressMap();
-	void *pAddress = pDeviceHandleMap->query(primeHandle);
-	if(pAddress != nullptr)
-	{
-		*pBuffer = pAddress;
-		pDeviceHandleMap->incrementReference(primeHandle);
-	}
-	else
-	{
-		// map memory
-		void *ptr;
-		VkResult result = mapMemory(primeHandle, &ptr);
-		if(result != VK_SUCCESS)
-		{
-			return result;
-		}
-
-		// Add primeHandle and ptr to deviceHandleMap
-		pDeviceHandleMap->add(primeHandle, ptr);
-		*pBuffer = pDeviceHandleMap->query(primeHandle);
-	}
-
-	return VK_SUCCESS;
-}
-
-void AHardwareBufferExternalMemory::deallocate(void *buffer, size_t size)
-{
-	if(ahb != nullptr)
-	{
-		const native_handle_t *h = AHardwareBuffer_getNativeHandle(ahb);
-		uint32_t primeHandle;
-		VkResult result = getPrimeHandle(h, primeHandle);
-		ASSERT(result == VK_SUCCESS);
-
-		// close gpu memory and rendernodeFD
-		vk::Device::AHBAddressMap *pDeviceHandleMap = device->getAHBAddressMap();
-		if(pDeviceHandleMap->decrementReference(primeHandle) == 0)
-		{
-			closeMemory(primeHandle);
-		}
-		close(rendernodeFD);
-
-		AHardwareBuffer_release(ahb);
-		ahb = nullptr;
-	}
-}
-
-VkResult AHardwareBufferExternalMemory::exportAndroidHardwareBuffer(struct AHardwareBuffer **pAhb) const
-{
-	// Each call to vkGetMemoryAndroidHardwareBufferANDROID *must* return an Android hardware buffer with a new reference
-	// acquired in addition to the reference held by the VkDeviceMemory. To avoid leaking resources, the application *must*
-	// release the reference by calling AHardwareBuffer_release when it is no longer needed.
-	AHardwareBuffer_acquire(ahb);
-	*pAhb = ahb;
-	return VK_SUCCESS;
-}
-
-uint32_t AHardwareBufferExternalMemory::GetAndroidHardwareBufferDescFormat(VkFormat format)
+uint32_t GetAHBFormatFromVkFormat(VkFormat format)
 {
 	switch(format)
 	{
@@ -204,7 +64,7 @@
 	}
 }
 
-VkFormat AHardwareBufferExternalMemory::GetVkFormat(uint32_t ahbFormat)
+VkFormat GetVkFormatFromAHBFormat(uint32_t ahbFormat)
 {
 	switch(ahbFormat)
 	{
@@ -244,8 +104,94 @@
 	}
 }
 
-VkFormatFeatureFlags AHardwareBufferExternalMemory::GetVkFormatFeatures(VkFormat format)
+uint64_t GetAHBLockUsageFromVkImageUsageFlags(VkImageUsageFlags flags)
 {
+	uint64_t usage = 0;
+
+	if(flags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT ||
+	   flags & VK_IMAGE_USAGE_SAMPLED_BIT ||
+	   flags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)
+	{
+		usage |= AHARDWAREBUFFER_USAGE_CPU_READ_MASK;
+	}
+
+	if(flags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ||
+	   flags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)
+	{
+		usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
+	}
+
+	return usage;
+}
+
+uint64_t GetAHBLockUsageFromVkBufferUsageFlags(VkBufferUsageFlags flags)
+{
+	uint64_t usage = 0;
+
+	if(flags & VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
+	{
+		usage |= AHARDWAREBUFFER_USAGE_CPU_READ_MASK;
+	}
+
+	if(flags & VK_BUFFER_USAGE_TRANSFER_DST_BIT)
+	{
+		usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
+	}
+
+	return usage;
+}
+
+uint64_t GetAHBUsageFromVkImageFlags(VkImageCreateFlags createFlags, VkImageUsageFlags usageFlags)
+{
+	uint64_t ahbUsage = 0;
+
+	if(usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT)
+	{
+		ahbUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
+	}
+	if(usageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
+	{
+		ahbUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
+	}
+	if(usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
+	{
+		ahbUsage |= AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+	}
+
+	if(createFlags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
+	{
+		ahbUsage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+	}
+	if(createFlags & VK_IMAGE_CREATE_PROTECTED_BIT)
+	{
+		ahbUsage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
+	}
+
+	// No usage bits set - set at least one GPU usage
+	if(ahbUsage == 0)
+	{
+		ahbUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+		           AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+	}
+
+	return ahbUsage;
+}
+
+uint64_t GetAHBUsageFromVkBufferFlags(VkBufferCreateFlags /*createFlags*/, VkBufferUsageFlags /*usageFlags*/)
+{
+	uint64_t ahbUsage = 0;
+
+	// TODO(b/141698760): needs fleshing out.
+	ahbUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+
+	return ahbUsage;
+}
+
+VkFormatFeatureFlags GetVkFormatFeaturesFromAHBFormat(uint32_t ahbFormat)
+{
+	VkFormatFeatureFlags features = 0;
+
+	VkFormat format = GetVkFormatFromAHBFormat(ahbFormat);
 	VkFormatProperties formatProperties;
 	vk::PhysicalDevice::GetFormatProperties(vk::Format(format), &formatProperties);
 
@@ -254,148 +200,14 @@
 	// TODO: b/167896057
 	//   The correct formatFeatureFlags depends on consumer and format
 	//   So this solution is incomplete without more information
-	return formatProperties.linearTilingFeatures | formatProperties.optimalTilingFeatures | formatProperties.bufferFeatures;
+	features |= formatProperties.linearTilingFeatures |
+	            formatProperties.optimalTilingFeatures |
+	            formatProperties.bufferFeatures;
+
+	return features;
 }
 
-VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferFormatProperties(const AHardwareBuffer_Desc &ahbDesc, VkAndroidHardwareBufferFormatPropertiesANDROID *pFormat)
-{
-
-	pFormat->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
-	pFormat->pNext = nullptr;
-
-	pFormat->format = GetVkFormat(ahbDesc.format);
-	pFormat->externalFormat = ahbDesc.format;
-	pFormat->formatFeatures = GetVkFormatFeatures(pFormat->format);
-
-	pFormat->samplerYcbcrConversionComponents = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
-	pFormat->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
-	pFormat->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
-	pFormat->suggestedXChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
-	pFormat->suggestedYChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
-
-	return VK_SUCCESS;
-}
-
-VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferProperties(VkDevice &device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties)
-{
-	AHardwareBuffer_Desc ahbDesc;
-	AHardwareBuffer_describe(buffer, &ahbDesc);
-
-	GetAndroidHardwareBufferFormatProperties(ahbDesc, (VkAndroidHardwareBufferFormatPropertiesANDROID *)pProperties->pNext);
-
-	const VkPhysicalDeviceMemoryProperties phyDeviceMemProps = vk::PhysicalDevice::GetMemoryProperties();
-	pProperties->memoryTypeBits = phyDeviceMemProps.memoryTypes[0].propertyFlags;
-
-	if(ahbDesc.format == AHARDWAREBUFFER_FORMAT_BLOB)
-	{
-		pProperties->allocationSize = ahbDesc.width;
-	}
-	else
-	{
-		VkImageCreateInfo info = {};
-		info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
-		info.pNext = nullptr;
-		info.flags = 0;
-		info.imageType = VK_IMAGE_TYPE_2D;
-		info.format = GetVkFormat(ahbDesc.format);
-		info.extent.width = ahbDesc.width;
-		info.extent.height = ahbDesc.height;
-		info.extent.depth = 1;
-		info.mipLevels = 1;
-		info.arrayLayers = 1;
-		info.samples = VK_SAMPLE_COUNT_1_BIT;
-		info.tiling = VK_IMAGE_TILING_OPTIMAL;
-		info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-
-		VkImage Image;
-		VkResult result = vk::Image::Create(vk::DEVICE_MEMORY, &info, &Image, vk::Cast(device));
-
-		pProperties->allocationSize = vk::Cast(Image)->getMemoryRequirements().size;
-		vk::destroy(Image, vk::DEVICE_MEMORY);
-	}
-
-	return VK_SUCCESS;
-}
-
-VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferUsageFromVkUsage(const VkImageCreateFlags createFlags, const VkImageUsageFlags usageFlags, uint64_t &ahbDescUsage)
-{
-	if(usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT)
-		ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
-	if(usageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
-		ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
-	if(usageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
-		ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
-
-	if(createFlags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
-		ahbDescUsage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
-	if(createFlags & VK_IMAGE_CREATE_PROTECTED_BIT)
-		ahbDescUsage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
-
-	// No usage bits set - set at least one GPU usage
-	if(ahbDescUsage == 0)
-		ahbDescUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
-
-	return VK_SUCCESS;
-}
-
-// Call into the native gralloc implementation to request a handle for the
-// rendernodeFD
-VkResult AHardwareBufferExternalMemory::getPrimeHandle(const native_handle_t *h, uint32_t &primeHandle)
-{
-	cros_gralloc_handle const *crosHandle = reinterpret_cast<cros_gralloc_handle const *>(h);
-	int ret = drmPrimeFDToHandle(rendernodeFD, crosHandle->fds[0], &primeHandle);
-	if(ret != 0)
-	{
-		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-	}
-
-	return VK_SUCCESS;
-}
-
-// Create a rendernodeFD
-void AHardwareBufferExternalMemory::createRenderNodeFD()
-{
-	rendernodeFD = drmOpenRender(128);
-}
-
-// using a primeHandle associated with a specific rendernodeFD, map a new block
-// of memory
-VkResult AHardwareBufferExternalMemory::mapMemory(uint32_t &primeHandle, void **ptr)
-{
-	drm_virtgpu_map map;
-	memset(&map, 0, sizeof(drm_virtgpu_map));
-	map.handle = primeHandle;
-	int ret = drmIoctl(rendernodeFD, DRM_IOCTL_VIRTGPU_MAP, &map);
-	if(ret != 0)
-	{
-		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-	}
-
-	// mmap it
-	*ptr = static_cast<unsigned char *>(
-	    mmap64(nullptr, 4096, PROT_WRITE, MAP_SHARED, rendernodeFD, map.offset));
-	if(ptr == MAP_FAILED)
-	{
-		return VK_ERROR_MEMORY_MAP_FAILED;
-	}
-
-	return VK_SUCCESS;
-}
-
-// Close out the memory block associated with rendernodeFD
-VkResult AHardwareBufferExternalMemory::closeMemory(uint32_t &primeHandle)
-{
-	struct drm_gem_close gem_close;
-	memset(&gem_close, 0x0, sizeof(gem_close));
-	gem_close.handle = primeHandle;
-	int ret = drmIoctl(rendernodeFD, DRM_IOCTL_GEM_CLOSE, &gem_close);
-	if(ret != 0)
-	{
-		return VK_ERROR_INVALID_EXTERNAL_HANDLE;
-	}
-
-	return VK_SUCCESS;
-}
+}  // namespace
 
 AHardwareBufferExternalMemory::AllocateInfo::AllocateInfo(const VkMemoryAllocateInfo *pAllocateInfo)
 {
@@ -415,11 +227,14 @@
 			{
 				const auto *exportInfo = reinterpret_cast<const VkExportMemoryAllocateInfo *>(createInfo);
 
-				if(exportInfo->handleTypes != VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
+				if(exportInfo->handleTypes == VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
+				{
+					exportAhb = true;
+				}
+				else
 				{
 					UNSUPPORTED("VkExportMemoryAllocateInfo::handleTypes %d", int(exportInfo->handleTypes));
 				}
-				exportAhb = true;
 			}
 			break;
 			case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO:
@@ -439,4 +254,208 @@
 	}
 }
 
+AHardwareBufferExternalMemory::AHardwareBufferExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo)
+    : allocateInfo(pAllocateInfo)
+{
+}
+
+AHardwareBufferExternalMemory::~AHardwareBufferExternalMemory()
+{
+	// correct deallocation of AHB does not require a pointer or size
+	deallocate(nullptr, 0);
+}
+
+// VkAllocateMemory
+VkResult AHardwareBufferExternalMemory::allocate(size_t /*size*/, void **pBuffer)
+{
+	if(allocateInfo.importAhb)
+	{
+		return importAndroidHardwareBuffer(allocateInfo.ahb, pBuffer);
+	}
+	else
+	{
+		ASSERT(allocateInfo.exportAhb);
+		return allocateAndroidHardwareBuffer(pBuffer);
+	}
+}
+
+void AHardwareBufferExternalMemory::deallocate(void *buffer, size_t size)
+{
+	if(ahb != nullptr)
+	{
+		unlockAndroidHardwareBuffer();
+
+		AHardwareBuffer_release(ahb);
+		ahb = nullptr;
+	}
+}
+
+VkResult AHardwareBufferExternalMemory::importAndroidHardwareBuffer(struct AHardwareBuffer *buffer, void **pBuffer)
+{
+	ahb = buffer;
+
+	AHardwareBuffer_acquire(ahb);
+
+	return lockAndroidHardwareBuffer(pBuffer);
+}
+
+VkResult AHardwareBufferExternalMemory::allocateAndroidHardwareBuffer(void **pBuffer)
+{
+	AHardwareBuffer_Desc desc = {};
+
+	if(allocateInfo.imageHandle)
+	{
+		vk::Image *image = allocateInfo.imageHandle;
+		ASSERT(image != nullptr);
+		ASSERT(image->getArrayLayers() == 1);
+
+		VkExtent3D extent = image->getExtent();
+
+		desc.width = extent.width;
+		desc.height = extent.height;
+		desc.layers = image->getArrayLayers();
+		desc.format = GetAHBFormatFromVkFormat(image->getFormat());
+		desc.usage = GetAHBUsageFromVkImageFlags(image->getFlags(), image->getUsage());
+	}
+	else
+	{
+		vk::Buffer *buffer = allocateInfo.bufferHandle;
+		ASSERT(buffer != nullptr);
+
+		desc.width = static_cast<uint32_t>(buffer->getSize());
+		desc.height = 1;
+		desc.layers = 1;
+		desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
+		desc.usage = GetAHBUsageFromVkBufferFlags(buffer->getFlags(), buffer->getUsage());
+	}
+
+	// create a new ahb from desc
+	int ret = AHardwareBuffer_allocate(&desc, &ahb);
+	if(ret != 0)
+	{
+		return VK_ERROR_OUT_OF_HOST_MEMORY;
+	}
+
+	return lockAndroidHardwareBuffer(pBuffer);
+}
+
+VkResult AHardwareBufferExternalMemory::lockAndroidHardwareBuffer(void **pBuffer)
+{
+	uint64_t usage = 0;
+	if(allocateInfo.imageHandle)
+	{
+		usage = GetAHBLockUsageFromVkImageUsageFlags(allocateInfo.imageHandle->getUsage());
+	}
+	else
+	{
+		usage = GetAHBLockUsageFromVkBufferUsageFlags(allocateInfo.bufferHandle->getUsage());
+	}
+
+	// Empty fence, lock immedietly.
+	int32_t fence = -1;
+
+	// Empty rect, lock entire buffer.
+	ARect *rect = nullptr;
+
+	int ret = AHardwareBuffer_lock(ahb, usage, fence, rect, pBuffer);
+	if(ret != 0)
+	{
+		return VK_ERROR_OUT_OF_HOST_MEMORY;
+	}
+
+	return VK_SUCCESS;
+}
+
+VkResult AHardwareBufferExternalMemory::unlockAndroidHardwareBuffer()
+{
+	int ret = AHardwareBuffer_unlock(ahb, /*fence=*/nullptr);
+	if(ret != 0)
+	{
+		return VK_ERROR_UNKNOWN;
+	}
+
+	return VK_SUCCESS;
+}
+
+VkResult AHardwareBufferExternalMemory::exportAndroidHardwareBuffer(struct AHardwareBuffer **pAhb) const
+{
+	// Each call to vkGetMemoryAndroidHardwareBufferANDROID *must* return an Android hardware buffer with a new reference
+	// acquired in addition to the reference held by the VkDeviceMemory. To avoid leaking resources, the application *must*
+	// release the reference by calling AHardwareBuffer_release when it is no longer needed.
+	AHardwareBuffer_acquire(ahb);
+	*pAhb = ahb;
+	return VK_SUCCESS;
+}
+
+VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferFormatProperties(const AHardwareBuffer_Desc &ahbDesc, VkAndroidHardwareBufferFormatPropertiesANDROID *pFormat)
+{
+	pFormat->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
+	pFormat->pNext = nullptr;
+	pFormat->format = GetVkFormatFromAHBFormat(ahbDesc.format);
+	pFormat->externalFormat = ahbDesc.format;
+	pFormat->formatFeatures = GetVkFormatFeaturesFromAHBFormat(ahbDesc.format);
+	pFormat->samplerYcbcrConversionComponents = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
+	pFormat->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
+	pFormat->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
+	pFormat->suggestedXChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
+	pFormat->suggestedYChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
+
+	return VK_SUCCESS;
+}
+
+VkResult AHardwareBufferExternalMemory::GetAndroidHardwareBufferProperties(VkDevice &device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties)
+{
+	VkResult result = VK_SUCCESS;
+
+	AHardwareBuffer_Desc ahbDesc;
+	AHardwareBuffer_describe(buffer, &ahbDesc);
+
+	if(pProperties->pNext != nullptr)
+	{
+		result = GetAndroidHardwareBufferFormatProperties(ahbDesc, (VkAndroidHardwareBufferFormatPropertiesANDROID *)pProperties->pNext);
+		if(result != VK_SUCCESS)
+		{
+			return result;
+		}
+	}
+
+	const VkPhysicalDeviceMemoryProperties phyDeviceMemProps = vk::PhysicalDevice::GetMemoryProperties();
+	pProperties->memoryTypeBits = phyDeviceMemProps.memoryTypes[0].propertyFlags;
+
+	if(ahbDesc.format == AHARDWAREBUFFER_FORMAT_BLOB)
+	{
+		pProperties->allocationSize = ahbDesc.width;
+	}
+	else
+	{
+		VkImageCreateInfo info = {};
+		info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
+		info.pNext = nullptr;
+		info.flags = 0;
+		info.imageType = VK_IMAGE_TYPE_2D;
+		info.format = GetVkFormatFromAHBFormat(ahbDesc.format);
+		info.extent.width = ahbDesc.width;
+		info.extent.height = ahbDesc.height;
+		info.extent.depth = 1;
+		info.mipLevels = 1;
+		info.arrayLayers = 1;
+		info.samples = VK_SAMPLE_COUNT_1_BIT;
+		info.tiling = VK_IMAGE_TILING_OPTIMAL;
+		info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+		VkImage Image;
+
+		result = vk::Image::Create(vk::DEVICE_MEMORY, &info, &Image, vk::Cast(device));
+		if(result != VK_SUCCESS)
+		{
+			return result;
+		}
+
+		pProperties->allocationSize = vk::Cast(Image)->getMemoryRequirements().size;
+		vk::destroy(Image, vk::DEVICE_MEMORY);
+	}
+
+	return result;
+}
+
 #endif  // SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
diff --git a/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp b/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
index 6292af5..9931daf 100644
--- a/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
+++ b/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
@@ -17,29 +17,14 @@
 
 #if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
 
+#	include "VkBuffer.hpp"
 #	include "VkDevice.hpp"
 #	include "VkDeviceMemory.hpp"
 #	include "VkDeviceMemoryExternalBase.hpp"
-
-#	include "VkBuffer.hpp"
-#	include "VkDeviceMemory.hpp"
 #	include "VkImage.hpp"
-#	include "VkStringify.hpp"
-
-#	include "System/Debug.hpp"
-#	include "System/Linux/MemFd.hpp"
 
 #	include <android/hardware_buffer.h>
 
-#	include <cros_gralloc/cros_gralloc_handle.h>
-#	include <unistd.h>
-#	include <virtgpu_drm.h>
-#	include <xf86drm.h>
-
-#	include <errno.h>
-#	include <string.h>
-#	include <map>
-
 class AHardwareBufferExternalMemory : public vk::DeviceMemory::ExternalBase
 {
 public:
@@ -68,49 +53,29 @@
 		return (info.importAhb || info.exportAhb) && (info.bufferHandle || info.imageHandle);
 	}
 
-	explicit AHardwareBufferExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo)
-	    : allocateInfo(pAllocateInfo)
-	{
-	}
-
+	explicit AHardwareBufferExternalMemory(const VkMemoryAllocateInfo *pAllocateInfo);
 	~AHardwareBufferExternalMemory();
+
 	VkResult allocate(size_t size, void **pBuffer) override;
 	void deallocate(void *buffer, size_t size) override;
-	VkResult allocateAndroidHardwareBuffer(size_t size, void **pBuffer);
 
-	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override
-	{
-		return typeFlagBit;
-	}
+	VkExternalMemoryHandleTypeFlagBits getFlagBit() const override { return typeFlagBit; }
 
 	VkResult exportAndroidHardwareBuffer(struct AHardwareBuffer **pAhb) const override;
 
-	void setDevicePtr(vk::Device *pDevice) override
-	{
-		device = pDevice;
-	}
+	void setDevicePtr(vk::Device *pDevice) override { device = pDevice; }
+	bool isAndroidHardwareBuffer() override { return true; }
 
-	bool isAndroidHardwareBuffer() override
-	{
-		return true;
-	}
-
-	static uint32_t GetAndroidHardwareBufferDescFormat(VkFormat format);
-	static VkFormat GetVkFormat(uint32_t ahbFormat);
-	static VkFormatFeatureFlags GetVkFormatFeatures(VkFormat format);
 	static VkResult GetAndroidHardwareBufferFormatProperties(const AHardwareBuffer_Desc &ahbDesc, VkAndroidHardwareBufferFormatPropertiesANDROID *pFormat);
 	static VkResult GetAndroidHardwareBufferProperties(VkDevice &device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties);
-	static VkResult GetAndroidHardwareBufferUsageFromVkUsage(const VkImageCreateFlags createFlags, const VkImageUsageFlags usageFlags, uint64_t &ahbDescUsage);
-
-	// All reliance on minigbm and DRM is contained in these functions
-	VkResult getPrimeHandle(const native_handle_t *h, uint32_t &primeHandle);
-	void createRenderNodeFD();
-	VkResult mapMemory(uint32_t &primeHandle, void **ptr);
-	VkResult closeMemory(uint32_t &primeHandle);
 
 private:
+	VkResult importAndroidHardwareBuffer(struct AHardwareBuffer *buffer, void **pBuffer);
+	VkResult allocateAndroidHardwareBuffer(void **pBuffer);
+	VkResult lockAndroidHardwareBuffer(void **pBuffer);
+	VkResult unlockAndroidHardwareBuffer();
+
 	struct AHardwareBuffer *ahb = nullptr;
-	int rendernodeFD;
 	vk::Device *device = nullptr;
 	AllocateInfo allocateInfo;
 };
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 97abeab..41977fd 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -529,108 +529,24 @@
 	TRACE("(VkPhysicalDevice physicalDevice = %p, VkFormat format = %d, VkImageType type = %d, VkImageTiling tiling = %d, VkImageUsageFlags usage = %d, VkImageCreateFlags flags = %d, VkImageFormatProperties* pImageFormatProperties = %p)",
 	      physicalDevice, (int)format, (int)type, (int)tiling, usage, flags, pImageFormatProperties);
 
-	// "If the combination of parameters to vkGetPhysicalDeviceImageFormatProperties is not supported by the implementation
-	//  for use in vkCreateImage, then all members of VkImageFormatProperties will be filled with zero."
-	memset(pImageFormatProperties, 0, sizeof(VkImageFormatProperties));
+	VkPhysicalDeviceImageFormatInfo2 info2 = {};
+	info2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
+	info2.pNext = nullptr;
+	info2.format = format;
+	info2.type = type;
+	info2.tiling = tiling;
+	info2.usage = usage;
+	info2.flags = flags;
 
-	VkFormatProperties properties;
-	vk::PhysicalDevice::GetFormatProperties(format, &properties);
+	VkImageFormatProperties2 properties2 = {};
+	properties2.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+	properties2.pNext = nullptr;
 
-	VkFormatFeatureFlags features;
-	switch(tiling)
-	{
-		case VK_IMAGE_TILING_LINEAR:
-			features = properties.linearTilingFeatures;
-			break;
+	VkResult result = vkGetPhysicalDeviceImageFormatProperties2(physicalDevice, &info2, &properties2);
 
-		case VK_IMAGE_TILING_OPTIMAL:
-			features = properties.optimalTilingFeatures;
-			break;
+	*pImageFormatProperties = properties2.imageFormatProperties;
 
-		default:
-			UNSUPPORTED("VkImageTiling %d", int(tiling));
-			features = 0;
-	}
-
-	if(features == 0)
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	// Check for usage conflict with features
-	if((usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(features & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) && !(features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) && !(features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) && !(features & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	if((usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) && !(features & VK_FORMAT_FEATURE_TRANSFER_DST_BIT))
-	{
-		return VK_ERROR_FORMAT_NOT_SUPPORTED;
-	}
-
-	auto allRecognizedUsageBits = VK_IMAGE_USAGE_SAMPLED_BIT |
-	                              VK_IMAGE_USAGE_STORAGE_BIT |
-	                              VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
-	                              VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
-	                              VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
-	                              VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-	                              VK_IMAGE_USAGE_TRANSFER_DST_BIT |
-	                              VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
-	ASSERT(!(usage & ~(allRecognizedUsageBits)));
-
-	// "Images created with tiling equal to VK_IMAGE_TILING_LINEAR have further restrictions on their limits and capabilities
-	//  compared to images created with tiling equal to VK_IMAGE_TILING_OPTIMAL."
-	if(tiling == VK_IMAGE_TILING_LINEAR)
-	{
-		if(type != VK_IMAGE_TYPE_2D)
-		{
-			return VK_ERROR_FORMAT_NOT_SUPPORTED;
-		}
-
-		if(vk::Format(format).isDepth() || vk::Format(format).isStencil())
-		{
-			return VK_ERROR_FORMAT_NOT_SUPPORTED;
-		}
-	}
-
-	// "Images created with a format from one of those listed in Formats requiring sampler Y'CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views
-	//  have further restrictions on their limits and capabilities compared to images created with other formats."
-	if(vk::Format(format).isYcbcrFormat())
-	{
-		if(type != VK_IMAGE_TYPE_2D)
-		{
-			return VK_ERROR_FORMAT_NOT_SUPPORTED;
-		}
-	}
-
-	vk::Cast(physicalDevice)->getImageFormatProperties(format, type, tiling, usage, flags, pImageFormatProperties);
-
-	return VK_SUCCESS;
+	return result;
 }
 
 VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties)
@@ -3044,6 +2960,10 @@
 	TRACE("(VkPhysicalDevice physicalDevice = %p, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo = %p, VkImageFormatProperties2* pImageFormatProperties = %p)",
 	      physicalDevice, pImageFormatInfo, pImageFormatProperties);
 
+	// "If the combination of parameters to vkGetPhysicalDeviceImageFormatProperties is not supported by the implementation
+	//  for use in vkCreateImage, then all members of VkImageFormatProperties will be filled with zero."
+	memset(&pImageFormatProperties->imageFormatProperties, 0, sizeof(VkImageFormatProperties));
+
 	const VkBaseInStructure *extensionFormatInfo = reinterpret_cast<const VkBaseInStructure *>(pImageFormatInfo->pNext);
 
 	const VkExternalMemoryHandleTypeFlagBits *handleType = nullptr;
@@ -3085,6 +3005,10 @@
 
 	VkBaseOutStructure *extensionProperties = reinterpret_cast<VkBaseOutStructure *>(pImageFormatProperties->pNext);
 
+#ifdef __ANDROID__
+	bool hasAHBUsage = false;
+#endif
+
 	while(extensionProperties)
 	{
 		switch(extensionProperties->sType)
@@ -3112,6 +3036,7 @@
 			{
 				auto properties = reinterpret_cast<VkAndroidHardwareBufferUsageANDROID *>(extensionProperties);
 				vk::Cast(physicalDevice)->getProperties(properties);
+				hasAHBUsage = true;
 			}
 			break;
 #endif
@@ -3123,13 +3048,119 @@
 		extensionProperties = extensionProperties->pNext;
 	}
 
-	return vkGetPhysicalDeviceImageFormatProperties(physicalDevice,
-	                                                pImageFormatInfo->format,
-	                                                pImageFormatInfo->type,
-	                                                pImageFormatInfo->tiling,
-	                                                pImageFormatInfo->usage,
-	                                                pImageFormatInfo->flags,
-	                                                &(pImageFormatProperties->imageFormatProperties));
+	VkFormat format = pImageFormatInfo->format;
+	VkImageType type = pImageFormatInfo->type;
+	VkImageTiling tiling = pImageFormatInfo->tiling;
+	VkImageUsageFlags usage = pImageFormatInfo->usage;
+	VkImageCreateFlags flags = pImageFormatInfo->flags;
+
+	VkFormatProperties properties;
+	vk::PhysicalDevice::GetFormatProperties(format, &properties);
+
+	VkFormatFeatureFlags features;
+	switch(tiling)
+	{
+		case VK_IMAGE_TILING_LINEAR:
+			features = properties.linearTilingFeatures;
+			break;
+
+		case VK_IMAGE_TILING_OPTIMAL:
+			features = properties.optimalTilingFeatures;
+			break;
+
+		default:
+			UNSUPPORTED("VkImageTiling %d", int(tiling));
+			features = 0;
+	}
+
+	if(features == 0)
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	// Check for usage conflict with features
+	if((usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(features & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) && !(features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) && !(features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) && !(features & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	if((usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) && !(features & VK_FORMAT_FEATURE_TRANSFER_DST_BIT))
+	{
+		return VK_ERROR_FORMAT_NOT_SUPPORTED;
+	}
+
+	auto allRecognizedUsageBits = VK_IMAGE_USAGE_SAMPLED_BIT |
+	                              VK_IMAGE_USAGE_STORAGE_BIT |
+	                              VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+	                              VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
+	                              VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
+	                              VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+	                              VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+	                              VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
+	ASSERT(!(usage & ~(allRecognizedUsageBits)));
+
+	// "Images created with tiling equal to VK_IMAGE_TILING_LINEAR have further restrictions on their limits and capabilities
+	//  compared to images created with tiling equal to VK_IMAGE_TILING_OPTIMAL."
+	if(tiling == VK_IMAGE_TILING_LINEAR)
+	{
+		if(type != VK_IMAGE_TYPE_2D)
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+
+		if(vk::Format(format).isDepth() || vk::Format(format).isStencil())
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+	}
+
+	// "Images created with a format from one of those listed in Formats requiring sampler Y'CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views
+	//  have further restrictions on their limits and capabilities compared to images created with other formats."
+	if(vk::Format(format).isYcbcrFormat())
+	{
+		if(type != VK_IMAGE_TYPE_2D)
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+	}
+
+	vk::Cast(physicalDevice)->getImageFormatProperties(format, type, tiling, usage, flags, &pImageFormatProperties->imageFormatProperties);
+
+#ifdef __ANDROID__
+	if(hasAHBUsage)
+	{
+		// AHardwareBuffer_lock may only be called with a single layer.
+		pImageFormatProperties->imageFormatProperties.maxArrayLayers = 1;
+		pImageFormatProperties->imageFormatProperties.maxMipLevels = 1;
+	}
+#endif
+
+	return VK_SUCCESS;
 }
 
 VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties)