Handle image properties from external device memory

Some external device memories really represent existing images
with requirements and not just memory blobs. For AHB, we want
to use the stride from the gralloc buffer.

Bug: b/141698760
Bug: b/147316305
Test: launch_cvd && cts -m CtsDeqpTestCases
  dEQP-VK.api.external.memory.android_hardware_buffer.*
Change-Id: If657c91e458545b58d1d3b0f15f807ca9828b09c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/50068
Presubmit-Ready: Jason Macnak <natsu@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Jason Macnak <natsu@google.com>
diff --git a/src/Vulkan/VkDeviceMemory.cpp b/src/Vulkan/VkDeviceMemory.cpp
index fd47bf5..81b10dc 100644
--- a/src/Vulkan/VkDeviceMemory.cpp
+++ b/src/Vulkan/VkDeviceMemory.cpp
@@ -306,6 +306,23 @@
 	return (supportedHandleTypes & handle_type_bit) != 0;
 }
 
+bool DeviceMemory::hasExternalImageProperties() const
+{
+	return external && external->hasExternalImageProperties();
+}
+
+int DeviceMemory::externalImageRowPitchBytes() const
+{
+	if(external)
+	{
+		return external->externalImageRowPitchBytes();
+	}
+
+	// This function should never be called on non-external memory.
+	ASSERT(false);
+	return -1;
+}
+
 #if SWIFTSHADER_EXTERNAL_MEMORY_OPAQUE_FD
 VkResult DeviceMemory::exportFd(int *pFd) const
 {
diff --git a/src/Vulkan/VkDeviceMemory.hpp b/src/Vulkan/VkDeviceMemory.hpp
index fa04e6a..ed685d5 100644
--- a/src/Vulkan/VkDeviceMemory.hpp
+++ b/src/Vulkan/VkDeviceMemory.hpp
@@ -57,6 +57,9 @@
 	// Internal implementation class for external memory. Platform-specific.
 	class ExternalBase;
 
+	bool hasExternalImageProperties() const;
+	int externalImageRowPitchBytes() const;
+
 private:
 	void *buffer = nullptr;
 	VkDeviceSize size = 0;
diff --git a/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp b/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
index 80e3250..7daa09b 100644
--- a/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
+++ b/src/Vulkan/VkDeviceMemoryExternalAndroid.cpp
@@ -28,6 +28,39 @@
 
 namespace {
 
+int GetBytesFromAHBFormat(uint32_t ahbFormat)
+{
+	switch(ahbFormat)
+	{
+		case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+			return 2;
+		case AHARDWAREBUFFER_FORMAT_D24_UNORM:
+			return 3;
+		case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
+			return 4;
+		case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+			return 4;
+		case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+			return 8;
+		case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+			return 2;
+		case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+			return 4;
+		case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+			return 4;
+		case AHARDWAREBUFFER_FORMAT_S8_UINT:
+			return 1;
+		default:
+			// TODO(b/165302991)
+			// - AHARDWAREBUFFER_FORMAT_BLOB
+			// - AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT
+			// - AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT
+			// - AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM
+			UNSUPPORTED("Requested bytes() for unsupported format %d", int(ahbFormat));
+			return -1;
+	}
+}
+
 uint32_t GetAHBFormatFromVkFormat(VkFormat format)
 {
 	switch(format)
@@ -295,14 +328,13 @@
 	ahb = buffer;
 
 	AHardwareBuffer_acquire(ahb);
+	AHardwareBuffer_describe(ahb, &ahbDesc);
 
 	return lockAndroidHardwareBuffer(pBuffer);
 }
 
 VkResult AHardwareBufferExternalMemory::allocateAndroidHardwareBuffer(void **pBuffer)
 {
-	AHardwareBuffer_Desc desc = {};
-
 	if(allocateInfo.imageHandle)
 	{
 		vk::Image *image = allocateInfo.imageHandle;
@@ -311,31 +343,32 @@
 
 		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());
+		ahbDesc.width = extent.width;
+		ahbDesc.height = extent.height;
+		ahbDesc.layers = image->getArrayLayers();
+		ahbDesc.format = GetAHBFormatFromVkFormat(image->getFormat());
+		ahbDesc.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());
+		ahbDesc.width = static_cast<uint32_t>(buffer->getSize());
+		ahbDesc.height = 1;
+		ahbDesc.layers = 1;
+		ahbDesc.format = AHARDWAREBUFFER_FORMAT_BLOB;
+		ahbDesc.usage = GetAHBUsageFromVkBufferFlags(buffer->getFlags(), buffer->getUsage());
 	}
 
-	// create a new ahb from desc
-	int ret = AHardwareBuffer_allocate(&desc, &ahb);
+	int ret = AHardwareBuffer_allocate(&ahbDesc, &ahb);
 	if(ret != 0)
 	{
 		return VK_ERROR_OUT_OF_HOST_MEMORY;
 	}
 
+	AHardwareBuffer_describe(ahb, &ahbDesc);
+
 	return lockAndroidHardwareBuffer(pBuffer);
 }
 
@@ -458,4 +491,9 @@
 	return result;
 }
 
+int AHardwareBufferExternalMemory::externalImageRowPitchBytes() const
+{
+	return GetBytesFromAHBFormat(ahbDesc.format) * ahbDesc.stride;
+}
+
 #endif  // SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
diff --git a/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp b/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
index 9931daf..73eb51a 100644
--- a/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
+++ b/src/Vulkan/VkDeviceMemoryExternalAndroid.hpp
@@ -69,6 +69,9 @@
 	static VkResult GetAndroidHardwareBufferFormatProperties(const AHardwareBuffer_Desc &ahbDesc, VkAndroidHardwareBufferFormatPropertiesANDROID *pFormat);
 	static VkResult GetAndroidHardwareBufferProperties(VkDevice &device, const struct AHardwareBuffer *buffer, VkAndroidHardwareBufferPropertiesANDROID *pProperties);
 
+	bool hasExternalImageProperties() const override final { return true; }
+	int externalImageRowPitchBytes() const override final;
+
 private:
 	VkResult importAndroidHardwareBuffer(struct AHardwareBuffer *buffer, void **pBuffer);
 	VkResult allocateAndroidHardwareBuffer(void **pBuffer);
@@ -76,6 +79,7 @@
 	VkResult unlockAndroidHardwareBuffer();
 
 	struct AHardwareBuffer *ahb = nullptr;
+	AHardwareBuffer_Desc ahbDesc = {};
 	vk::Device *device = nullptr;
 	AllocateInfo allocateInfo;
 };
diff --git a/src/Vulkan/VkDeviceMemoryExternalBase.hpp b/src/Vulkan/VkDeviceMemoryExternalBase.hpp
index 428678e..198945e 100644
--- a/src/Vulkan/VkDeviceMemoryExternalBase.hpp
+++ b/src/Vulkan/VkDeviceMemoryExternalBase.hpp
@@ -57,6 +57,11 @@
 	}
 #endif
 
+	// Some external device memories, such as Android hardware buffers, represent
+	// specific images with requirements.
+	virtual bool hasExternalImageProperties() const { return false; }
+	virtual int externalImageRowPitchBytes() const { return 0; }
+
 protected:
 	ExternalBase() = default;
 };
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 966dac3..897aae3 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -749,6 +749,11 @@
 
 int Image::rowPitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const
 {
+	if(deviceMemory && deviceMemory->hasExternalImageProperties())
+	{
+		return deviceMemory->externalImageRowPitchBytes();
+	}
+
 	// Depth and Stencil pitch should be computed separately
 	ASSERT((aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=
 	       (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));