SpirvShader: Implement OpImageQueryLevels

Tests: dEQP-VK.glsl.texture_functions.query.texturequerylevels.*
Bug: b/129523279
Change-Id: I385eaa4a8d5fc50c12c7cf284c26037ed93aa469
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31109
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 3999d3d..6d17256 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -901,6 +901,7 @@
 			case spv::OpImageFetch:
 			case spv::OpImageQuerySize:
 			case spv::OpImageQuerySizeLod:
+			case spv::OpImageQueryLevels:
 			case spv::OpImageRead:
 			case spv::OpImageTexelPointer:
 			case spv::OpGroupNonUniformElect:
@@ -2490,6 +2491,9 @@
 		case spv::OpImageQuerySizeLod:
 			return EmitImageQuerySizeLod(insn, state);
 
+		case spv::OpImageQueryLevels:
+			return EmitImageQueryLevels(insn, state);
+
 		case spv::OpImageRead:
 			return EmitImageRead(insn, state);
 
@@ -4895,6 +4899,38 @@
 		}
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitImageQueryLevels(InsnIterator insn, EmitState *state) const
+	{
+		auto &resultTy = getType(Type::ID(insn.word(1)));
+		ASSERT(resultTy.sizeInComponents == 1);
+		auto resultId = Object::ID(insn.word(2));
+		auto imageId = Object::ID(insn.word(3));
+
+		const DescriptorDecorations &d = descriptorDecorations.at(imageId);
+		auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+		auto &bindingLayout = setLayout->getBindingLayout(d.Binding);
+
+		Pointer<Byte> descriptor = state->routine->getPointer(imageId).base;
+		Int mipLevels = 0;
+		switch (bindingLayout.descriptorType)
+		{
+		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+		{
+			mipLevels = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, mipLevels)); // uint32_t
+			break;
+		}
+		default:
+			UNREACHABLE("Image descriptorType: %d", int(bindingLayout.descriptorType));
+		}
+
+		auto &dst = state->routine->createIntermediate(resultId, 1);
+		dst.move(0, SIMD::Int(mipLevels));
+
+		return EmitResult::Continue;
+	}
+
 	SIMD::Pointer SpirvShader::GetTexelAddress(SpirvRoutine const *routine, SIMD::Pointer ptr, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId, bool useStencilAspect) const
 	{
 		bool isArrayed = imageType.definition.word(5) != 0;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 21a9d5a..2c98e4f 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -941,6 +941,7 @@
 		EmitResult EmitImageSample(ImageInstruction instruction, InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageQuerySize(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageQuerySizeLod(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitImageQueryLevels(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageRead(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageWrite(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageTexelPointer(InsnIterator insn, EmitState *state) const;
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index 5356880..dfb8362 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -314,6 +314,7 @@
 			auto numElements = bufferView->getElementCount();
 			imageSampler[i].extent = { numElements, 1, 1 };
 			imageSampler[i].arrayLayers = 1;
+			imageSampler[i].mipLevels = 1;
 			imageSampler[i].texture.widthWidthHeightHeight = sw::vector(numElements, numElements, 1, 1);
 			imageSampler[i].texture.width = sw::replicate(numElements);
 			imageSampler[i].texture.height = sw::replicate(1);
@@ -351,6 +352,7 @@
 			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].type = imageView->getType();
 			imageSampler[i].swizzle = imageView->getComponentMapping();
 			imageSampler[i].format = imageView->getFormat(ImageView::SAMPLING);
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 7a96b44..c368b1d 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -41,6 +41,7 @@
 	alignas(16) sw::Texture texture;
 	VkExtent3D extent; // Of base mip-level.
 	int arrayLayers;
+	int mipLevels;
 };
 
 struct alignas(16) StorageImageDescriptor