Fix ImageView size computation

Erroneous ImageView size computation was causing out of bounds
memory accesses whenever an ImageView only represents a subsection
of an Image. The getSizeInBytes() function now only returns the
size in bytes within the Image object from the beginning to the
end of the bytes used by the ImageView.

Bug: b/150464740
Change-Id: I0985af9cdfbb85b10336a7691f76009496bb2ae5
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43329
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index 704f6f8..64de3bf 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -449,7 +449,7 @@
 			descriptor[i].slicePitchBytes = descriptor[i].samplePitchBytes * imageView->getSampleCount();
 			descriptor[i].arrayLayers = imageView->getSubresourceRange().layerCount;
 			descriptor[i].sampleCount = imageView->getSampleCount();
-			descriptor[i].sizeInBytes = static_cast<int>(imageView->getImageSizeInBytes());
+			descriptor[i].sizeInBytes = static_cast<int>(imageView->getSizeInBytes());
 
 			if(imageView->getFormat().isStencil())
 			{
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 4c6e110..93e07b0 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -178,6 +178,42 @@
 	return memoryRequirements;
 }
 
+size_t Image::getSizeInBytes(const VkImageSubresourceRange &subresourceRange) const
+{
+	size_t size = 0;
+	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
+	uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
+	uint32_t layerCount = lastLayer - subresourceRange.baseArrayLayer + 1;
+	uint32_t mipLevelCount = lastMipLevel - subresourceRange.baseMipLevel + 1;
+
+	auto aspect = static_cast<VkImageAspectFlagBits>(subresourceRange.aspectMask);
+
+	if(layerCount > 1)
+	{
+		if(mipLevelCount < mipLevels)  // Compute size for all layers except the last one, then add relevant mip level sizes only for last layer
+		{
+			size = (layerCount - 1) * getLayerSize(aspect);
+			for(uint32_t mipLevel = subresourceRange.baseMipLevel; mipLevel <= lastMipLevel; ++mipLevel)
+			{
+				size += getMultiSampledLevelSize(aspect, mipLevel);
+			}
+		}
+		else  // All mip levels used, compute full layer sizes
+		{
+			size = layerCount * getLayerSize(aspect);
+		}
+	}
+	else  // Single layer, add all mip levels in the subresource range
+	{
+		for(uint32_t mipLevel = subresourceRange.baseMipLevel; mipLevel <= lastMipLevel; ++mipLevel)
+		{
+			size += getMultiSampledLevelSize(aspect, mipLevel);
+		}
+	}
+
+	return size;
+}
+
 bool Image::canBindToMemory(DeviceMemory *pDeviceMemory) const
 {
 	return pDeviceMemory->checkExternalMemoryHandleType(supportedExternalMemoryHandleTypes);
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 23092e4..99860be 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -51,6 +51,7 @@
 	static size_t ComputeRequiredAllocationSize(const VkImageCreateInfo *pCreateInfo);
 
 	const VkMemoryRequirements getMemoryRequirements() const;
+	size_t getSizeInBytes(const VkImageSubresourceRange &subresourceRange) const;
 	void getSubresourceLayout(const VkImageSubresource *pSubresource, VkSubresourceLayout *pLayout) const;
 	void bind(DeviceMemory *pDeviceMemory, VkDeviceSize pMemoryOffset);
 	void copyTo(Image *dstImage, const VkImageCopy &pRegion) const;
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 08ed4af..2993950 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -110,7 +110,7 @@
 
 	const VkComponentMapping &getComponentMapping() const { return components; }
 	const VkImageSubresourceRange &getSubresourceRange() const { return subresourceRange; }
-	size_t getImageSizeInBytes() const { return image->getMemoryRequirements().size; }
+	size_t getSizeInBytes() const { return image->getSizeInBytes(subresourceRange); }
 
 private:
 	bool imageTypesMatch(VkImageType imageType) const;