Cleanup: using the proper Image subregion structure

VkImage use different structures to represent subregions:
- VkImageSubresource (Single mip level, single layer)
- VkImageSubresourceLayers (Single mip level, multiple layers)
- VkImageSubresourceRange (Multiple mip levels, multiple layers)

This cl changes the subregion structure used in vk::Image and in the
Blitter so that it contains the information required for the current
task and no more than that.

Bug: b/159045555
Change-Id: I186a90ff321318319c01a0718a95bbae2d2eb26a
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/45788
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index 3b761d4..5794e52 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -85,11 +85,10 @@
 		return;
 	}
 
-	VkImageSubresourceLayers subresLayers = {
+	VkImageSubresource subres = {
 		subresourceRange.aspectMask,
 		subresourceRange.baseMipLevel,
-		subresourceRange.baseArrayLayer,
-		1
+		subresourceRange.baseArrayLayer
 	};
 
 	uint32_t lastMipLevel = dest->getLastMipLevel(subresourceRange);
@@ -102,9 +101,9 @@
 		area = *renderArea;
 	}
 
-	for(; subresLayers.mipLevel <= lastMipLevel; subresLayers.mipLevel++)
+	for(; subres.mipLevel <= lastMipLevel; subres.mipLevel++)
 	{
-		VkExtent3D extent = dest->getMipLevelExtent(aspect, subresLayers.mipLevel);
+		VkExtent3D extent = dest->getMipLevelExtent(aspect, subres.mipLevel);
 		if(!renderArea)
 		{
 			area.extent.width = extent.width;
@@ -114,10 +113,10 @@
 		BlitData data = {
 			pixel, nullptr,  // source, dest
 
-			format.bytes(),                                        // sPitchB
-			dest->rowPitchBytes(aspect, subresLayers.mipLevel),    // dPitchB
-			0,                                                     // sSliceB (unused in clear operations)
-			dest->slicePitchBytes(aspect, subresLayers.mipLevel),  // dSliceB
+			format.bytes(),                                  // sPitchB
+			dest->rowPitchBytes(aspect, subres.mipLevel),    // dPitchB
+			0,                                               // sSliceB (unused in clear operations)
+			dest->slicePitchBytes(aspect, subres.mipLevel),  // dSliceB
 
 			0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f,  // x0, y0, z0, w, h, d
 
@@ -133,21 +132,20 @@
 		if(renderArea && dest->is3DSlice())
 		{
 			// Reinterpret layers as depth slices
-			subresLayers.baseArrayLayer = 0;
-			subresLayers.layerCount = 1;
+			subres.arrayLayer = 0;
 			for(uint32_t depth = subresourceRange.baseArrayLayer; depth <= lastLayer; depth++)
 			{
-				data.dest = dest->getTexelPointer({ 0, 0, static_cast<int32_t>(depth) }, subresLayers);
+				data.dest = dest->getTexelPointer({ 0, 0, static_cast<int32_t>(depth) }, subres);
 				blitRoutine(&data);
 			}
 		}
 		else
 		{
-			for(subresLayers.baseArrayLayer = subresourceRange.baseArrayLayer; subresLayers.baseArrayLayer <= lastLayer; subresLayers.baseArrayLayer++)
+			for(subres.arrayLayer = subresourceRange.baseArrayLayer; subres.arrayLayer <= lastLayer; subres.arrayLayer++)
 			{
 				for(uint32_t depth = 0; depth < extent.depth; depth++)
 				{
-					data.dest = dest->getTexelPointer({ 0, 0, static_cast<int32_t>(depth) }, subresLayers);
+					data.dest = dest->getTexelPointer({ 0, 0, static_cast<int32_t>(depth) }, subres);
 
 					blitRoutine(&data);
 				}
@@ -208,11 +206,10 @@
 			return false;
 	}
 
-	VkImageSubresourceLayers subresLayers = {
+	VkImageSubresource subres = {
 		subresourceRange.aspectMask,
 		subresourceRange.baseMipLevel,
-		subresourceRange.baseArrayLayer,
-		1
+		subresourceRange.baseArrayLayer
 	};
 	uint32_t lastMipLevel = dest->getLastMipLevel(subresourceRange);
 	uint32_t lastLayer = dest->getLastLayerIndex(subresourceRange);
@@ -224,11 +221,11 @@
 		area = *renderArea;
 	}
 
-	for(; subresLayers.mipLevel <= lastMipLevel; subresLayers.mipLevel++)
+	for(; subres.mipLevel <= lastMipLevel; subres.mipLevel++)
 	{
-		int rowPitchBytes = dest->rowPitchBytes(aspect, subresLayers.mipLevel);
-		int slicePitchBytes = dest->slicePitchBytes(aspect, subresLayers.mipLevel);
-		VkExtent3D extent = dest->getMipLevelExtent(aspect, subresLayers.mipLevel);
+		int rowPitchBytes = dest->rowPitchBytes(aspect, subres.mipLevel);
+		int slicePitchBytes = dest->slicePitchBytes(aspect, subres.mipLevel);
+		VkExtent3D extent = dest->getMipLevelExtent(aspect, subres.mipLevel);
 		if(!renderArea)
 		{
 			area.extent.width = extent.width;
@@ -239,12 +236,12 @@
 			extent.depth = 1;  // The 3D image is instead interpreted as a 2D image with layers
 		}
 
-		for(subresLayers.baseArrayLayer = subresourceRange.baseArrayLayer; subresLayers.baseArrayLayer <= lastLayer; subresLayers.baseArrayLayer++)
+		for(subres.arrayLayer = subresourceRange.baseArrayLayer; subres.arrayLayer <= lastLayer; subres.arrayLayer++)
 		{
 			for(uint32_t depth = 0; depth < extent.depth; depth++)
 			{
 				uint8_t *slice = (uint8_t *)dest->getTexelPointer(
-				    { area.offset.x, area.offset.y, static_cast<int32_t>(depth) }, subresLayers);
+				    { area.offset.x, area.offset.y, static_cast<int32_t>(depth) }, subres);
 
 				for(int j = 0; j < dest->getSampleCountFlagBits(); j++)
 				{
@@ -1745,8 +1742,11 @@
 		false,  // filter3D
 	};
 
-	VkImageSubresourceLayers srcSubresLayers = subresource;
-	srcSubresLayers.layerCount = 1;
+	VkImageSubresource srcSubres = {
+		subresource.aspectMask,
+		subresource.mipLevel,
+		subresource.baseArrayLayer
+	};
 
 	VkImageSubresourceRange srcSubresRange = {
 		subresource.aspectMask,
@@ -1758,9 +1758,9 @@
 
 	uint32_t lastLayer = src->getLastLayerIndex(srcSubresRange);
 
-	for(; srcSubresLayers.baseArrayLayer <= lastLayer; srcSubresLayers.baseArrayLayer++)
+	for(; srcSubres.arrayLayer <= lastLayer; srcSubres.arrayLayer++)
 	{
-		data.source = src->getTexelPointer({ 0, 0, 0 }, srcSubresLayers);
+		data.source = src->getTexelPointer({ 0, 0, 0 }, srcSubres);
 		ASSERT(data.source < src->end());
 		blitRoutine(&data);
 	}
@@ -1807,8 +1807,11 @@
 		false,  // filter3D
 	};
 
-	VkImageSubresourceLayers dstSubresLayers = subresource;
-	dstSubresLayers.layerCount = 1;
+	VkImageSubresource dstSubres = {
+		subresource.aspectMask,
+		subresource.mipLevel,
+		subresource.baseArrayLayer
+	};
 
 	VkImageSubresourceRange dstSubresRange = {
 		subresource.aspectMask,
@@ -1820,9 +1823,9 @@
 
 	uint32_t lastLayer = dst->getLastLayerIndex(dstSubresRange);
 
-	for(; dstSubresLayers.baseArrayLayer <= lastLayer; dstSubresLayers.baseArrayLayer++)
+	for(; dstSubres.arrayLayer <= lastLayer; dstSubres.arrayLayer++)
 	{
-		data.dest = dst->getTexelPointer({ 0, 0, 0 }, dstSubresLayers);
+		data.dest = dst->getTexelPointer({ 0, 0, 0 }, dstSubres);
 		ASSERT(data.dest < dst->end());
 		blitRoutine(&data);
 	}
@@ -1927,34 +1930,32 @@
 		false,  // filter3D
 	};
 
-	VkImageSubresourceLayers srcSubresLayers = {
+	VkImageSubresource srcSubres = {
 		region.srcSubresource.aspectMask,
 		region.srcSubresource.mipLevel,
-		region.srcSubresource.baseArrayLayer,
-		1
+		region.srcSubresource.baseArrayLayer
 	};
 
-	VkImageSubresourceLayers dstSubresLayers = {
+	VkImageSubresource dstSubres = {
 		region.dstSubresource.aspectMask,
 		region.dstSubresource.mipLevel,
-		region.dstSubresource.baseArrayLayer,
-		1
+		region.dstSubresource.baseArrayLayer
 	};
 
-	VkImageSubresourceRange srcSubresRange = {
-		region.srcSubresource.aspectMask,
-		region.srcSubresource.mipLevel,
+	VkImageSubresourceRange dstSubresRange = {
+		region.dstSubresource.aspectMask,
+		region.dstSubresource.mipLevel,
 		1,
-		region.srcSubresource.baseArrayLayer,
-		region.srcSubresource.layerCount
+		region.dstSubresource.baseArrayLayer,
+		region.dstSubresource.layerCount
 	};
 
-	uint32_t lastLayer = src->getLastLayerIndex(srcSubresRange);
+	uint32_t lastLayer = src->getLastLayerIndex(dstSubresRange);
 
-	for(; srcSubresLayers.baseArrayLayer <= lastLayer; srcSubresLayers.baseArrayLayer++, dstSubresLayers.baseArrayLayer++)
+	for(; dstSubres.arrayLayer <= lastLayer; srcSubres.arrayLayer++, dstSubres.arrayLayer++)
 	{
-		data.source = src->getTexelPointer({ 0, 0, 0 }, srcSubresLayers);
-		data.dest = dst->getTexelPointer({ 0, 0, 0 }, dstSubresLayers);
+		data.source = src->getTexelPointer({ 0, 0, 0 }, srcSubres);
+		data.dest = dst->getTexelPointer({ 0, 0, 0 }, dstSubres);
 
 		ASSERT(data.source < src->end());
 		ASSERT(data.dest < dst->end());
@@ -2011,25 +2012,24 @@
 	return function("BlitRoutine");
 }
 
-void Blitter::updateBorders(vk::Image *image, const VkImageSubresourceLayers &subresourceLayers)
+void Blitter::updateBorders(vk::Image *image, const VkImageSubresource &subresource)
 {
-	ASSERT(image->getArrayLayers() >= (subresourceLayers.baseArrayLayer + 6));
+	ASSERT(image->getArrayLayers() >= (subresource.arrayLayer + 6));
 
 	// From Vulkan 1.1 spec, section 11.5. Image Views:
 	// "For cube and cube array image views, the layers of the image view starting
 	//  at baseArrayLayer correspond to faces in the order +X, -X, +Y, -Y, +Z, -Z."
-	VkImageSubresourceLayers posX = subresourceLayers;
-	posX.layerCount = 1;
-	VkImageSubresourceLayers negX = posX;
-	negX.baseArrayLayer++;
-	VkImageSubresourceLayers posY = negX;
-	posY.baseArrayLayer++;
-	VkImageSubresourceLayers negY = posY;
-	negY.baseArrayLayer++;
-	VkImageSubresourceLayers posZ = negY;
-	posZ.baseArrayLayer++;
-	VkImageSubresourceLayers negZ = posZ;
-	negZ.baseArrayLayer++;
+	VkImageSubresource posX = subresource;
+	VkImageSubresource negX = posX;
+	negX.arrayLayer++;
+	VkImageSubresource posY = negX;
+	posY.arrayLayer++;
+	VkImageSubresource negY = posY;
+	negY.arrayLayer++;
+	VkImageSubresource posZ = negY;
+	posZ.arrayLayer++;
+	VkImageSubresource negZ = posZ;
+	negZ.arrayLayer++;
 
 	// Copy top / bottom
 	copyCubeEdge(image, posX, BOTTOM, negY, RIGHT);
@@ -2062,7 +2062,7 @@
 	copyCubeEdge(image, negZ, LEFT, posX, RIGHT);
 
 	// Compute corner colors
-	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresourceLayers.aspectMask);
+	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresource.aspectMask);
 	vk::Format format = image->getFormat(aspect);
 	VkSampleCountFlagBits samples = image->getSampleCountFlagBits();
 	State state(format, format, samples, samples, Options{ 0xF });
@@ -2077,10 +2077,10 @@
 		return;
 	}
 
-	VkExtent3D extent = image->getMipLevelExtent(aspect, subresourceLayers.mipLevel);
+	VkExtent3D extent = image->getMipLevelExtent(aspect, subresource.mipLevel);
 	CubeBorderData data = {
 		image->getTexelPointer({ 0, 0, 0 }, posX),
-		image->rowPitchBytes(aspect, subresourceLayers.mipLevel),
+		image->rowPitchBytes(aspect, subresource.mipLevel),
 		static_cast<uint32_t>(image->getLayerSize(aspect)),
 		extent.width
 	};
@@ -2088,14 +2088,12 @@
 }
 
 void Blitter::copyCubeEdge(vk::Image *image,
-                           const VkImageSubresourceLayers &dstSubresourceLayers, Edge dstEdge,
-                           const VkImageSubresourceLayers &srcSubresourceLayers, Edge srcEdge)
+                           const VkImageSubresource &dstSubresource, Edge dstEdge,
+                           const VkImageSubresource &srcSubresource, Edge srcEdge)
 {
-	ASSERT(srcSubresourceLayers.aspectMask == dstSubresourceLayers.aspectMask);
-	ASSERT(srcSubresourceLayers.mipLevel == dstSubresourceLayers.mipLevel);
-	ASSERT(srcSubresourceLayers.baseArrayLayer != dstSubresourceLayers.baseArrayLayer);
-	ASSERT(srcSubresourceLayers.layerCount == 1);
-	ASSERT(dstSubresourceLayers.layerCount == 1);
+	ASSERT(srcSubresource.aspectMask == dstSubresource.aspectMask);
+	ASSERT(srcSubresource.mipLevel == dstSubresource.mipLevel);
+	ASSERT(srcSubresource.arrayLayer != dstSubresource.arrayLayer);
 
 	// Figure out if the edges to be copied in reverse order respectively from one another
 	// The copy should be reversed whenever the same edges are contiguous or if we're
@@ -2111,11 +2109,11 @@
 	               ((srcEdge == BOTTOM) && (dstEdge == LEFT)) ||
 	               ((srcEdge == LEFT) && (dstEdge == BOTTOM));
 
-	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(srcSubresourceLayers.aspectMask);
+	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(srcSubresource.aspectMask);
 	int bytes = image->getFormat(aspect).bytes();
-	int pitchB = image->rowPitchBytes(aspect, srcSubresourceLayers.mipLevel);
+	int pitchB = image->rowPitchBytes(aspect, srcSubresource.mipLevel);
 
-	VkExtent3D extent = image->getMipLevelExtent(aspect, srcSubresourceLayers.mipLevel);
+	VkExtent3D extent = image->getMipLevelExtent(aspect, srcSubresource.mipLevel);
 	int w = extent.width;
 	int h = extent.height;
 	if(w != h)
@@ -2143,8 +2141,8 @@
 		dstOffset.y += reverse ? h : 1;
 	}
 
-	const uint8_t *src = static_cast<const uint8_t *>(image->getTexelPointer(srcOffset, srcSubresourceLayers));
-	uint8_t *dst = static_cast<uint8_t *>(image->getTexelPointer(dstOffset, dstSubresourceLayers));
+	const uint8_t *src = static_cast<const uint8_t *>(image->getTexelPointer(srcOffset, srcSubresource));
+	uint8_t *dst = static_cast<uint8_t *>(image->getTexelPointer(dstOffset, dstSubresource));
 	ASSERT((src < image->end()) && ((src + (w * srcDelta)) < image->end()));
 	ASSERT((dst < image->end()) && ((dst + (w * dstDelta)) < image->end()));
 
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 2e6f946..316d7ec 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -147,7 +147,7 @@
 	void blitToBuffer(const vk::Image *src, VkImageSubresourceLayers subresource, VkOffset3D offset, VkExtent3D extent, uint8_t *dst, int bufferRowPitch, int bufferSlicePitch);
 	void blitFromBuffer(const vk::Image *dst, VkImageSubresourceLayers subresource, VkOffset3D offset, VkExtent3D extent, uint8_t *src, int bufferRowPitch, int bufferSlicePitch);
 
-	void updateBorders(vk::Image *image, const VkImageSubresourceLayers &subresourceLayers);
+	void updateBorders(vk::Image *image, const VkImageSubresource &subresource);
 
 private:
 	enum Edge
@@ -185,8 +185,8 @@
 	void computeCubeCorner(Pointer<Byte> &layer, Int &x0, Int &x1, Int &y0, Int &y1, Int &pitchB, const State &state);
 
 	void copyCubeEdge(vk::Image *image,
-	                  const VkImageSubresourceLayers &dstSubresourceLayers, Edge dstEdge,
-	                  const VkImageSubresourceLayers &srcSubresourceLayers, Edge srcEdge);
+	                  const VkImageSubresource &dstSubresource, Edge dstEdge,
+	                  const VkImageSubresource &srcSubresource, Edge srcEdge);
 
 	marl::mutex blitMutex;
 	RoutineCache<State, BlitFunction::CFunctionType> blitCache GUARDED_BY(blitMutex);
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index e2fbac4..1995d78 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -345,8 +345,8 @@
 	int srcBytesPerBlock = srcFormat.bytesPerBlock();
 	ASSERT(srcBytesPerBlock == dstFormat.bytesPerBlock());
 
-	const uint8_t *srcMem = static_cast<const uint8_t *>(getTexelPointer(region.srcOffset, region.srcSubresource));
-	uint8_t *dstMem = static_cast<uint8_t *>(dstImage->getTexelPointer(region.dstOffset, region.dstSubresource));
+	const uint8_t *srcMem = static_cast<const uint8_t *>(getTexelPointer(region.srcOffset, { region.srcSubresource.aspectMask, region.srcSubresource.mipLevel, region.srcSubresource.baseArrayLayer }));
+	uint8_t *dstMem = static_cast<uint8_t *>(dstImage->getTexelPointer(region.dstOffset, { region.dstSubresource.aspectMask, region.dstSubresource.mipLevel, region.dstSubresource.baseArrayLayer }));
 
 	int srcRowPitchBytes = rowPitchBytes(srcAspect, region.srcSubresource.mipLevel);
 	int srcSlicePitchBytes = slicePitchBytes(srcAspect, region.srcSubresource.mipLevel);
@@ -460,7 +460,7 @@
 	int bufferSlicePitchBytes = bufferExtent.height * bufferRowPitchBytes;
 
 	uint8_t *bufferMemory = static_cast<uint8_t *>(buffer->getOffsetPointer(region.bufferOffset));
-	uint8_t *imageMemory = static_cast<uint8_t *>(getTexelPointer(region.imageOffset, region.imageSubresource));
+	uint8_t *imageMemory = static_cast<uint8_t *>(getTexelPointer(region.imageOffset, { region.imageSubresource.aspectMask, region.imageSubresource.mipLevel, region.imageSubresource.baseArrayLayer }));
 	uint8_t *srcMemory = bufferIsSource ? bufferMemory : imageMemory;
 	uint8_t *dstMemory = bufferIsSource ? imageMemory : bufferMemory;
 	int imageRowPitchBytes = rowPitchBytes(aspect, region.imageSubresource.mipLevel);
@@ -574,11 +574,11 @@
 	copy(srcBuffer, region, true);
 }
 
-void *Image::getTexelPointer(const VkOffset3D &offset, const VkImageSubresourceLayers &subresource) const
+void *Image::getTexelPointer(const VkOffset3D &offset, const VkImageSubresource &subresource) const
 {
 	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresource.aspectMask);
 	return deviceMemory->getOffsetPointer(texelOffsetBytesInStorage(offset, subresource) +
-	                                      getMemoryOffset(aspect, subresource.mipLevel, subresource.baseArrayLayer));
+	                                      getMemoryOffset(aspect, subresource.mipLevel, subresource.arrayLayer));
 }
 
 VkExtent3D Image::imageExtentInBlocks(const VkExtent3D &extent, VkImageAspectFlagBits aspect) const
@@ -652,7 +652,7 @@
 	return (isCube() && !format.isCompressed()) ? 1 : 0;
 }
 
-VkDeviceSize Image::texelOffsetBytesInStorage(const VkOffset3D &offset, const VkImageSubresourceLayers &subresource) const
+VkDeviceSize Image::texelOffsetBytesInStorage(const VkOffset3D &offset, const VkImageSubresource &subresource) const
 {
 	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresource.aspectMask);
 	VkOffset3D adjustedOffset = imageOffsetInBlocks(offset, aspect);
@@ -1012,6 +1012,40 @@
 
 void Image::prepareForSampling(const VkImageSubresourceRange &subresourceRange)
 {
+	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
+	uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
+
+	VkImageSubresource subresource = {
+		subresourceRange.aspectMask,
+		subresourceRange.baseMipLevel,
+		subresourceRange.baseArrayLayer
+	};
+
+	// First, decompress all relevant dirty subregions
+	for(subresource.arrayLayer = subresourceRange.baseArrayLayer;
+	    subresource.arrayLayer <= lastLayer;
+	    subresource.arrayLayer++)
+	{
+		for(subresource.mipLevel = subresourceRange.baseMipLevel;
+		    subresource.mipLevel <= lastMipLevel;
+		    subresource.mipLevel++)
+		{
+			decompress(subresource);
+		}
+	}
+
+	// Second, update cubemap borders
+	subresource.arrayLayer = subresourceRange.baseArrayLayer;
+	for(subresource.mipLevel = subresourceRange.baseMipLevel;
+	    subresource.mipLevel <= lastMipLevel;
+	    subresource.mipLevel++)
+	{
+		updateCube(subresource);
+	}
+}
+
+void Image::decompress(const VkImageSubresource &subresource)
+{
 	if(decompressedImage)
 	{
 		switch(format)
@@ -1026,7 +1060,7 @@
 			case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
 			case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
 			case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
-				decodeETC2(subresourceRange);
+				decodeETC2(subresource);
 				break;
 			case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
 			case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
@@ -1044,7 +1078,7 @@
 			case VK_FORMAT_BC6H_SFLOAT_BLOCK:
 			case VK_FORMAT_BC7_UNORM_BLOCK:
 			case VK_FORMAT_BC7_SRGB_BLOCK:
-				decodeBC(subresourceRange);
+				decodeBC(subresource);
 				break;
 			case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
 			case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
@@ -1088,119 +1122,91 @@
 			case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
 			case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
 			case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
-				decodeASTC(subresourceRange);
+				decodeASTC(subresource);
 				break;
 			default:
 				break;
 		}
 	}
+}
 
+void Image::updateCube(const VkImageSubresource &subres)
+{
 	if(isCube() && (arrayLayers >= 6))
 	{
-		VkImageSubresourceLayers subresourceLayers = {
-			subresourceRange.aspectMask,
-			subresourceRange.baseMipLevel,
-			subresourceRange.baseArrayLayer,
-			6
-		};
+		VkImageSubresource subresource = subres;
 
 		// Update the borders of all the groups of 6 layers that can be part of a cubemaps but don't
 		// touch leftover layers that cannot be part of cubemaps.
-		uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
-		for(; subresourceLayers.mipLevel <= lastMipLevel; subresourceLayers.mipLevel++)
+		for(subresource.arrayLayer = 0; subresource.arrayLayer < arrayLayers - 5; subresource.arrayLayer += 6)
 		{
-			for(subresourceLayers.baseArrayLayer = 0;
-			    subresourceLayers.baseArrayLayer < arrayLayers - 5;
-			    subresourceLayers.baseArrayLayer += 6)
-			{
-				device->getBlitter()->updateBorders(decompressedImage ? decompressedImage : this, subresourceLayers);
-			}
+			device->getBlitter()->updateBorders(decompressedImage ? decompressedImage : this, subresource);
 		}
 	}
 }
 
-void Image::decodeETC2(const VkImageSubresourceRange &subresourceRange) const
+void Image::decodeETC2(const VkImageSubresource &subresource)
 {
 	ASSERT(decompressedImage);
 
 	ETC_Decoder::InputType inputType = GetInputType(format);
 
-	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
-	uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
-
 	int bytes = decompressedImage->format.bytes();
 	bool fakeAlpha = (format == VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK) || (format == VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK);
 	size_t sizeToWrite = 0;
 
-	VkImageSubresourceLayers subresourceLayers = { subresourceRange.aspectMask, subresourceRange.baseMipLevel, subresourceRange.baseArrayLayer, 1 };
-	for(; subresourceLayers.baseArrayLayer <= lastLayer; subresourceLayers.baseArrayLayer++)
+	VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresource.aspectMask), subresource.mipLevel);
+
+	int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresource.mipLevel);
+
+	if(fakeAlpha)
 	{
-		for(; subresourceLayers.mipLevel <= lastMipLevel; subresourceLayers.mipLevel++)
+		// To avoid overflow in case of cube textures, which are offset in memory to account for the border,
+		// compute the size from the first pixel to the last pixel, excluding any padding or border before
+		// the first pixel or after the last pixel.
+		sizeToWrite = ((mipLevelExtent.height - 1) * pitchB) + (mipLevelExtent.width * bytes);
+	}
+
+	for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
+	{
+		uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresource));
+		uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresource));
+
+		if(fakeAlpha)
 		{
-			VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceLayers.aspectMask), subresourceLayers.mipLevel);
-
-			int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresourceLayers.mipLevel);
-
-			if(fakeAlpha)
-			{
-				// To avoid overflow in case of cube textures, which are offset in memory to account for the border,
-				// compute the size from the first pixel to the last pixel, excluding any padding or border before
-				// the first pixel or after the last pixel.
-				sizeToWrite = ((mipLevelExtent.height - 1) * pitchB) + (mipLevelExtent.width * bytes);
-			}
-
-			for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
-			{
-				uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresourceLayers));
-				uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresourceLayers));
-
-				if(fakeAlpha)
-				{
-					ASSERT((dest + sizeToWrite) < decompressedImage->end());
-					memset(dest, 0xFF, sizeToWrite);
-				}
-
-				ETC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
-				                    pitchB, bytes, inputType);
-			}
+			ASSERT((dest + sizeToWrite) < decompressedImage->end());
+			memset(dest, 0xFF, sizeToWrite);
 		}
+
+		ETC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
+		                    pitchB, bytes, inputType);
 	}
 }
 
-void Image::decodeBC(const VkImageSubresourceRange &subresourceRange) const
+void Image::decodeBC(const VkImageSubresource &subresource)
 {
 	ASSERT(decompressedImage);
 
 	int n = GetBCn(format);
 	int noAlphaU = GetNoAlphaOrUnsigned(format);
 
-	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
-	uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
-
 	int bytes = decompressedImage->format.bytes();
 
-	VkImageSubresourceLayers subresourceLayers = { subresourceRange.aspectMask, subresourceRange.baseMipLevel, subresourceRange.baseArrayLayer, 1 };
-	for(; subresourceLayers.baseArrayLayer <= lastLayer; subresourceLayers.baseArrayLayer++)
+	VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresource.aspectMask), subresource.mipLevel);
+
+	int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresource.mipLevel);
+
+	for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
 	{
-		for(; subresourceLayers.mipLevel <= lastMipLevel; subresourceLayers.mipLevel++)
-		{
-			VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceLayers.aspectMask), subresourceLayers.mipLevel);
+		uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresource));
+		uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresource));
 
-			int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresourceLayers.mipLevel);
-
-			for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
-			{
-				uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresourceLayers));
-				uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresourceLayers));
-
-				BC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
-				                   pitchB, bytes, n, noAlphaU);
-			}
-		}
+		BC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height,
+		                   pitchB, bytes, n, noAlphaU);
 	}
 }
 
-void Image::decodeASTC(const VkImageSubresourceRange &subresourceRange) const
+void Image::decodeASTC(const VkImageSubresource &subresource)
 {
 	ASSERT(decompressedImage);
 
@@ -1209,39 +1215,29 @@
 	int zBlockSize = 1;
 	bool isUnsigned = format.isUnsignedComponent(0);
 
-	uint32_t lastLayer = getLastLayerIndex(subresourceRange);
-	uint32_t lastMipLevel = getLastMipLevel(subresourceRange);
-
 	int bytes = decompressedImage->format.bytes();
 
-	VkImageSubresourceLayers subresourceLayers = { subresourceRange.aspectMask, subresourceRange.baseMipLevel, subresourceRange.baseArrayLayer, 1 };
-	for(; subresourceLayers.baseArrayLayer <= lastLayer; subresourceLayers.baseArrayLayer++)
+	VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresource.aspectMask), subresource.mipLevel);
+
+	int xblocks = (mipLevelExtent.width + xBlockSize - 1) / xBlockSize;
+	int yblocks = (mipLevelExtent.height + yBlockSize - 1) / yBlockSize;
+	int zblocks = (zBlockSize > 1) ? (mipLevelExtent.depth + zBlockSize - 1) / zBlockSize : 1;
+
+	if(xblocks <= 0 || yblocks <= 0 || zblocks <= 0)
 	{
-		for(; subresourceLayers.mipLevel <= lastMipLevel; subresourceLayers.mipLevel++)
-		{
-			VkExtent3D mipLevelExtent = getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceLayers.aspectMask), subresourceLayers.mipLevel);
+		return;
+	}
 
-			int xblocks = (mipLevelExtent.width + xBlockSize - 1) / xBlockSize;
-			int yblocks = (mipLevelExtent.height + yBlockSize - 1) / yBlockSize;
-			int zblocks = (zBlockSize > 1) ? (mipLevelExtent.depth + zBlockSize - 1) / zBlockSize : 1;
+	int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresource.mipLevel);
+	int sliceB = decompressedImage->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresource.mipLevel);
 
-			if(xblocks <= 0 || yblocks <= 0 || zblocks <= 0)
-			{
-				continue;
-			}
+	for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
+	{
+		uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresource));
+		uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresource));
 
-			int pitchB = decompressedImage->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresourceLayers.mipLevel);
-			int sliceB = decompressedImage->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, subresourceLayers.mipLevel);
-
-			for(int32_t depth = 0; depth < static_cast<int32_t>(mipLevelExtent.depth); depth++)
-			{
-				uint8_t *source = static_cast<uint8_t *>(getTexelPointer({ 0, 0, depth }, subresourceLayers));
-				uint8_t *dest = static_cast<uint8_t *>(decompressedImage->getTexelPointer({ 0, 0, depth }, subresourceLayers));
-
-				ASTC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height, mipLevelExtent.depth, bytes, pitchB, sliceB,
-				                     xBlockSize, yBlockSize, zBlockSize, xblocks, yblocks, zblocks, isUnsigned);
-			}
-		}
+		ASTC_Decoder::Decode(source, dest, mipLevelExtent.width, mipLevelExtent.height, mipLevelExtent.depth, bytes, pitchB, sliceB,
+		                     xBlockSize, yBlockSize, zBlockSize, xblocks, yblocks, zblocks, isUnsigned);
 	}
 }
 
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 0401df3..e29efb0 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -77,7 +77,7 @@
 	VkExtent3D getMipLevelExtent(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	int rowPitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	int slicePitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
-	void *getTexelPointer(const VkOffset3D &offset, const VkImageSubresourceLayers &subresource) const;
+	void *getTexelPointer(const VkOffset3D &offset, const VkImageSubresource &subresource) const;
 	bool isCube() const;
 	bool is3DSlice() const;
 	uint8_t *end() const;
@@ -104,7 +104,7 @@
 	VkDeviceSize getLayerOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
-	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D &offset, const VkImageSubresourceLayers &subresource) const;
+	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D &offset, const VkImageSubresource &subresource) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect) const;
 	VkExtent3D imageExtentInBlocks(const VkExtent3D &extent, VkImageAspectFlagBits aspect) const;
 	VkOffset3D imageOffsetInBlocks(const VkOffset3D &offset, VkImageAspectFlagBits aspect) const;
@@ -112,9 +112,11 @@
 	VkFormat getClearFormat() const;
 	void clear(void *pixelData, VkFormat pixelFormat, const vk::Format &viewFormat, const VkImageSubresourceRange &subresourceRange, const VkRect2D &renderArea);
 	int borderSize() const;
-	void decodeETC2(const VkImageSubresourceRange &subresourceRange) const;
-	void decodeBC(const VkImageSubresourceRange &subresourceRange) const;
-	void decodeASTC(const VkImageSubresourceRange &subresourceRange) const;
+	void decompress(const VkImageSubresource &subresource);
+	void updateCube(const VkImageSubresource &subresource);
+	void decodeETC2(const VkImageSubresource &subresource);
+	void decodeBC(const VkImageSubresource &subresource);
+	void decodeASTC(const VkImageSubresource &subresource);
 
 	const Device *const device = nullptr;
 	DeviceMemory *deviceMemory = nullptr;
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index 4b8880d..37a8aa0 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -296,14 +296,13 @@
 {
 	ASSERT(mipLevel < subresourceRange.levelCount);
 
-	VkImageSubresourceLayers imageSubresourceLayers = {
+	VkImageSubresource imageSubresource = {
 		static_cast<VkImageAspectFlags>(aspect),
 		subresourceRange.baseMipLevel + mipLevel,
 		subresourceRange.baseArrayLayer + layer,
-		subresourceRange.layerCount
 	};
 
-	return getImage(usage)->getTexelPointer(offset, imageSubresourceLayers);
+	return getImage(usage)->getTexelPointer(offset, imageSubresource);
 }
 
 }  // namespace vk