Refactor image query instructions

OpImageQuerySize[Lod] SPIR-V instructions were previously implemented
by storing both the image depth and layer count in the descriptor. Since
3D textures can't be arrayed, we only need one field. Also avoid the
division by 6 for cube array layers in the instruction implementation,
by performing it during the descriptor update instead.

This reflects a similar change made by
https://swiftshader-review.googlesource.com/c/SwiftShader/+/47388

Bug: b/162315264
Change-Id: Iaa787e6c131eec2da7e4a404743ef722423305d5
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47688
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index 59316c7..efd62fa 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -374,62 +374,58 @@
 
 	ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
 	bool isArrayed = imageType.definition.word(5) != 0;
-	bool isCubeMap = imageType.definition.word(3) == spv::DimCube;
+	uint32_t dimensions = resultTy.componentCount - (isArrayed ? 1 : 0);
 
 	const DescriptorDecorations &d = descriptorDecorations.at(imageId);
 	auto descriptorType = routine->pipelineLayout->getDescriptorType(d.DescriptorSet, d.Binding);
 
 	Pointer<Byte> descriptor = state->getPointer(imageId).base;
 
-	Pointer<Int> extent;
-	Int arrayLayers;
+	Int width;
+	Int height;
+	Int depth;
 
 	switch(descriptorType)
 	{
 		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
 		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
-		{
-			extent = descriptor + OFFSET(vk::StorageImageDescriptor, extent);                           // int[3]*
-			arrayLayers = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, arrayLayers));  // uint32_t
+			width = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, width));
+			height = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, height));
+			depth = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, depth));
 			break;
-		}
 		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
 		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
 		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
-		{
-			extent = descriptor + OFFSET(vk::SampledImageDescriptor, extent);                           // int[3]*
-			arrayLayers = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, arrayLayers));  // uint32_t
+			width = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, width));
+			height = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, height));
+			depth = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, depth));
 			break;
-		}
 		default:
 			UNREACHABLE("Image descriptorType: %d", int(descriptorType));
 	}
 
-	auto dimensions = resultTy.componentCount - (isArrayed ? 1 : 0);
-	std::vector<Int> out;
 	if(lodId != 0)
 	{
 		auto lodVal = Operand(this, state, lodId);
 		ASSERT(lodVal.componentCount == 1);
 		auto lod = lodVal.Int(0);
 		auto one = SIMD::Int(1);
-		for(uint32_t i = 0; i < dimensions; i++)
-		{
-			dst.move(i, Max(SIMD::Int(extent[i]) >> lod, one));
-		}
+
+		if(dimensions >= 1) dst.move(0, Max(SIMD::Int(width) >> lod, one));
+		if(dimensions >= 2) dst.move(1, Max(SIMD::Int(height) >> lod, one));
+		if(dimensions >= 3) dst.move(2, Max(SIMD::Int(depth) >> lod, one));
 	}
 	else
 	{
-		for(uint32_t i = 0; i < dimensions; i++)
-		{
-			dst.move(i, SIMD::Int(extent[i]));
-		}
+
+		if(dimensions >= 1) dst.move(0, SIMD::Int(width));
+		if(dimensions >= 2) dst.move(1, SIMD::Int(height));
+		if(dimensions >= 3) dst.move(2, SIMD::Int(depth));
 	}
 
 	if(isArrayed)
 	{
-		auto numElements = isCubeMap ? (arrayLayers / 6) : RValue<Int>(arrayLayers);
-		dst.move(dimensions, SIMD::Int(numElements));
+		dst.move(dimensions, SIMD::Int(depth));
 	}
 }
 
@@ -570,20 +566,20 @@
 	// Other out-of-bounds behaviors work properly by just comparing the offset against the total size.
 	if(outOfBoundsBehavior == OutOfBoundsBehavior::Nullify)
 	{
-		auto width = SIMD::UInt(*Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, extent.width)));
+		SIMD::UInt width = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, width));
 		SIMD::Int oobMask = As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(u), width));
 
 		if(dims > 1)
 		{
-			auto height = SIMD::UInt(*Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, extent.height)));
+			SIMD::UInt height = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, height));
 			oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(v), height));
 		}
 
 		if((dims > 2) || isArrayed)
 		{
-			auto depth = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, extent.depth));
-			auto arrayLayers = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, arrayLayers));
-			oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(w), SIMD::UInt(depth * arrayLayers)));  // TODO: Precompute extent. 3D image can't have layers.
+			UInt depth = *Pointer<UInt>(descriptor + OFFSET(vk::StorageImageDescriptor, depth));
+			if(dim == spv::DimCube) { depth *= 6; }
+			oobMask |= As<SIMD::Int>(CmpNLT(As<SIMD::UInt>(w), SIMD::UInt(depth)));
 		}
 
 		if(sampleId.value())
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index 27fbde8..6959fd7 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -264,7 +264,7 @@
 
 	if(entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER)
 	{
-		SampledImageDescriptor *imageSampler = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
+		SampledImageDescriptor *sampledImage = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
 
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
@@ -273,37 +273,38 @@
 			//  descriptorCount of zero, must all either use immutable samplers or must all not use immutable samplers."
 			if(!binding.immutableSamplers)
 			{
-				imageSampler[i].updateSampler(vk::Cast(update->sampler));
+				sampledImage[i].updateSampler(vk::Cast(update->sampler));
 			}
-			imageSampler[i].device = device;
+			sampledImage[i].device = device;
 		}
 	}
 	else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)
 	{
-		SampledImageDescriptor *imageSampler = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
+		SampledImageDescriptor *sampledImage = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
 
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
 			auto update = reinterpret_cast<VkBufferView const *>(src + entry.offset + entry.stride * i);
 			auto bufferView = vk::Cast(*update);
 
-			imageSampler[i].type = VK_IMAGE_VIEW_TYPE_1D;
-			imageSampler[i].imageViewId = bufferView->id;
-			imageSampler[i].swizzle = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
-			imageSampler[i].format = bufferView->getFormat();
+			sampledImage[i].type = VK_IMAGE_VIEW_TYPE_1D;
+			sampledImage[i].imageViewId = bufferView->id;
+			sampledImage[i].swizzle = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
+			sampledImage[i].format = bufferView->getFormat();
 
 			auto numElements = bufferView->getElementCount();
-			imageSampler[i].extent = { numElements, 1, 1 };
-			imageSampler[i].arrayLayers = 1;
-			imageSampler[i].mipLevels = 1;
-			imageSampler[i].sampleCount = 1;
-			imageSampler[i].texture.widthWidthHeightHeight = sw::float4(static_cast<float>(numElements), static_cast<float>(numElements), 1, 1);
-			imageSampler[i].texture.width = sw::float4(static_cast<float>(numElements));
-			imageSampler[i].texture.height = sw::float4(1);
-			imageSampler[i].texture.depth = sw::float4(1);
-			imageSampler[i].device = device;
+			sampledImage[i].width = numElements;
+			sampledImage[i].height = 1;
+			sampledImage[i].depth = 1;
+			sampledImage[i].mipLevels = 1;
+			sampledImage[i].sampleCount = 1;
+			sampledImage[i].texture.widthWidthHeightHeight = sw::float4(static_cast<float>(numElements), static_cast<float>(numElements), 1, 1);
+			sampledImage[i].texture.width = sw::float4(static_cast<float>(numElements));
+			sampledImage[i].texture.height = sw::float4(1);
+			sampledImage[i].texture.depth = sw::float4(1);
+			sampledImage[i].device = device;
 
-			sw::Mipmap &mipmap = imageSampler[i].texture.mipmap[0];
+			sw::Mipmap &mipmap = sampledImage[i].texture.mipmap[0];
 			mipmap.buffer = bufferView->getPointer();
 			mipmap.width[0] = mipmap.width[1] = mipmap.width[2] = mipmap.width[3] = numElements;
 			mipmap.height[0] = mipmap.height[1] = mipmap.height[2] = mipmap.height[3] = 1;
@@ -317,16 +318,16 @@
 	else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ||
 	        entry.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
 	{
-		SampledImageDescriptor *imageSampler = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
+		SampledImageDescriptor *sampledImage = reinterpret_cast<SampledImageDescriptor *>(memToWrite);
 
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
-			auto update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
+			auto *update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
 
 			vk::ImageView *imageView = vk::Cast(update->imageView);
 			Format format = imageView->getFormat(ImageView::SAMPLING);
 
-			sw::Texture *texture = &imageSampler[i].texture;
+			sw::Texture *texture = &sampledImage[i].texture;
 
 			if(entry.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
 			{
@@ -334,20 +335,23 @@
 				//  descriptorCount of zero, must all either use immutable samplers or must all not use immutable samplers."
 				if(!binding.immutableSamplers)
 				{
-					imageSampler[i].updateSampler(vk::Cast(update->sampler));
+					sampledImage[i].updateSampler(vk::Cast(update->sampler));
 				}
 			}
 
-			imageSampler[i].imageViewId = imageView->id;
-			imageSampler[i].extent = imageView->getMipLevelExtent(0);
-			imageSampler[i].arrayLayers = imageView->getSubresourceRange().layerCount;
-			imageSampler[i].mipLevels = imageView->getSubresourceRange().levelCount;
-			imageSampler[i].sampleCount = imageView->getSampleCount();
-			imageSampler[i].type = imageView->getType();
-			imageSampler[i].swizzle = imageView->getComponentMapping();
-			imageSampler[i].format = format;
-			imageSampler[i].device = device;
-			imageSampler[i].memoryOwner = imageView;
+			const auto &extent = imageView->getMipLevelExtent(0);
+
+			sampledImage[i].imageViewId = imageView->id;
+			sampledImage[i].width = extent.width;
+			sampledImage[i].height = extent.height;
+			sampledImage[i].depth = imageView->getDepthOrLayerCount(0);
+			sampledImage[i].mipLevels = imageView->getSubresourceRange().levelCount;
+			sampledImage[i].sampleCount = imageView->getSampleCount();
+			sampledImage[i].type = imageView->getType();
+			sampledImage[i].swizzle = imageView->getComponentMapping();
+			sampledImage[i].format = format;
+			sampledImage[i].device = device;
+			sampledImage[i].memoryOwner = imageView;
 
 			auto &subresourceRange = imageView->getSubresourceRange();
 
@@ -410,12 +414,11 @@
 
 					int width = extent.width;
 					int height = extent.height;
-					int layers = imageView->getSubresourceRange().layerCount;
-					int depth = layers > 1 ? layers : extent.depth;
-					if(imageView->getType() == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) depth /= 6;
+					int layerCount = imageView->getSubresourceRange().layerCount;
+					int depth = imageView->getDepthOrLayerCount(level);
 					int bytes = format.bytes();
 					int pitchP = imageView->rowPitchBytes(aspect, level, ImageView::SAMPLING) / bytes;
-					int sliceP = (layers > 1 ? imageView->layerPitchBytes(aspect, ImageView::SAMPLING) : imageView->slicePitchBytes(aspect, level, ImageView::SAMPLING)) / bytes;
+					int sliceP = (layerCount > 1 ? imageView->layerPitchBytes(aspect, ImageView::SAMPLING) : imageView->slicePitchBytes(aspect, level, ImageView::SAMPLING)) / bytes;
 					int samplePitchP = imageView->getMipLevelSize(aspect, level, ImageView::SAMPLING) / bytes;
 					int sampleMax = imageView->getSampleCount() - 1;
 
@@ -427,49 +430,54 @@
 	else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
 	        entry.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
 	{
-		auto descriptor = reinterpret_cast<StorageImageDescriptor *>(memToWrite);
+		auto storageImage = reinterpret_cast<StorageImageDescriptor *>(memToWrite);
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
-			auto update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
-			auto imageView = vk::Cast(update->imageView);
-			descriptor[i].ptr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
-			descriptor[i].extent = imageView->getMipLevelExtent(0);
-			descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			descriptor[i].samplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			descriptor[i].slicePitchBytes = imageView->getSubresourceRange().layerCount > 1
-			                                    ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT)
-			                                    : imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			descriptor[i].arrayLayers = imageView->getSubresourceRange().layerCount;
-			descriptor[i].sampleCount = imageView->getSampleCount();
-			descriptor[i].sizeInBytes = static_cast<int>(imageView->getSizeInBytes());
-			descriptor[i].memoryOwner = imageView;
+			auto *update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
+			auto *imageView = vk::Cast(update->imageView);
+			const auto &extent = imageView->getMipLevelExtent(0);
+			auto layerCount = imageView->getSubresourceRange().layerCount;
+
+			storageImage[i].ptr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
+			storageImage[i].width = extent.width;
+			storageImage[i].height = extent.height;
+			storageImage[i].depth = imageView->getDepthOrLayerCount(0);
+			storageImage[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			storageImage[i].samplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			storageImage[i].slicePitchBytes = layerCount > 1
+			                                      ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT)
+			                                      : imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			storageImage[i].sampleCount = imageView->getSampleCount();
+			storageImage[i].sizeInBytes = static_cast<int>(imageView->getSizeInBytes());
+			storageImage[i].memoryOwner = imageView;
 
 			if(imageView->getFormat().isStencil())
 			{
-				descriptor[i].stencilPtr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0);
-				descriptor[i].stencilRowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
-				descriptor[i].stencilSamplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
-				descriptor[i].stencilSlicePitchBytes = (imageView->getSubresourceRange().layerCount > 1)
-				                                           ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT)
-				                                           : imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
+				storageImage[i].stencilPtr = imageView->getOffsetPointer({ 0, 0, 0 }, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0);
+				storageImage[i].stencilRowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
+				storageImage[i].stencilSamplePitchBytes = imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
+				storageImage[i].stencilSlicePitchBytes = (imageView->getSubresourceRange().layerCount > 1)
+				                                             ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT)
+				                                             : imageView->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
 			}
 		}
 	}
 	else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
 	{
-		auto descriptor = reinterpret_cast<StorageImageDescriptor *>(memToWrite);
+		auto *storageImage = reinterpret_cast<StorageImageDescriptor *>(memToWrite);
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
 			auto update = reinterpret_cast<VkBufferView const *>(src + entry.offset + entry.stride * i);
 			auto bufferView = vk::Cast(*update);
-			descriptor[i].ptr = bufferView->getPointer();
-			descriptor[i].extent = { bufferView->getElementCount(), 1, 1 };
-			descriptor[i].rowPitchBytes = 0;
-			descriptor[i].slicePitchBytes = 0;
-			descriptor[i].samplePitchBytes = 0;
-			descriptor[i].arrayLayers = 1;
-			descriptor[i].sampleCount = 1;
-			descriptor[i].sizeInBytes = bufferView->getRangeInBytes();
+			storageImage[i].ptr = bufferView->getPointer();
+			storageImage[i].width = bufferView->getElementCount();
+			storageImage[i].height = 1;
+			storageImage[i].depth = 1;
+			storageImage[i].rowPitchBytes = 0;
+			storageImage[i].slicePitchBytes = 0;
+			storageImage[i].samplePitchBytes = 0;
+			storageImage[i].sampleCount = 1;
+			storageImage[i].sizeInBytes = bufferView->getRangeInBytes();
 		}
 	}
 	else if(entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
@@ -477,14 +485,14 @@
 	        entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
 	        entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)
 	{
-		auto descriptor = reinterpret_cast<BufferDescriptor *>(memToWrite);
+		auto *bufferDescriptor = reinterpret_cast<BufferDescriptor *>(memToWrite);
 		for(uint32_t i = 0; i < entry.descriptorCount; i++)
 		{
 			auto update = reinterpret_cast<VkDescriptorBufferInfo const *>(src + entry.offset + entry.stride * i);
 			auto buffer = vk::Cast(update->buffer);
-			descriptor[i].ptr = buffer->getOffsetPointer(update->offset);
-			descriptor[i].sizeInBytes = static_cast<int>((update->range == VK_WHOLE_SIZE) ? buffer->getSize() - update->offset : update->range);
-			descriptor[i].robustnessSize = static_cast<int>(buffer->getSize() - update->offset);
+			bufferDescriptor[i].ptr = buffer->getOffsetPointer(update->offset);
+			bufferDescriptor[i].sizeInBytes = static_cast<int>((update->range == VK_WHOLE_SIZE) ? buffer->getSize() - update->offset : update->range);
+			bufferDescriptor[i].robustnessSize = static_cast<int>(buffer->getSize() - update->offset);
 		}
 	}
 }
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 6c7e159..f1919b1 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -44,8 +44,9 @@
 	VkFormat format;
 	VkComponentMapping swizzle;
 	alignas(16) sw::Texture texture;
-	VkExtent3D extent;  // Of base mip-level.
-	int arrayLayers;
+	int width;  // Of base mip-level.
+	int height;
+	int depth;  // Layer/cube count for arrayed images
 	int mipLevels;
 	int sampleCount;
 
@@ -57,11 +58,12 @@
 	~StorageImageDescriptor() = delete;
 
 	void *ptr;
-	VkExtent3D extent;
+	int width;
+	int height;
+	int depth;  // Layer/cube count for arrayed images
 	int rowPitchBytes;
 	int slicePitchBytes;  // Layer pitch in case of array image
 	int samplePitchBytes;
-	int arrayLayers;
 	int sampleCount;
 	int sizeInBytes;
 
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index e8a6cad..421a37f 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "VkImageView.hpp"
+
 #include "VkImage.hpp"
 #include "System/Math.hpp"
 
@@ -286,6 +287,22 @@
 	                                subresourceRange.baseMipLevel + mipLevel);
 }
 
+int ImageView::getDepthOrLayerCount(uint32_t mipLevel) const
+{
+	VkExtent3D extent = getMipLevelExtent(mipLevel);
+	int layers = subresourceRange.layerCount;
+	int depthOrLayers = layers > 1 ? layers : extent.depth;
+
+	// For cube images the number of whole cubes is returned
+	if(viewType == VK_IMAGE_VIEW_TYPE_CUBE ||
+	   viewType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+	{
+		depthOrLayers /= 6;
+	}
+
+	return depthOrLayers;
+}
+
 void *ImageView::getOffsetPointer(const VkOffset3D &offset, VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer, Usage usage) const
 {
 	ASSERT(mipLevel < subresourceRange.levelCount);
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 297ffd1..eb377ab 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -88,6 +88,7 @@
 	int getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel, Usage usage = RAW) const;
 	int layerPitchBytes(VkImageAspectFlagBits aspect, Usage usage = RAW) const;
 	VkExtent3D getMipLevelExtent(uint32_t mipLevel) const;
+	int getDepthOrLayerCount(uint32_t mipLevel) const;
 
 	int getSampleCount() const
 	{