SpirvShader: Implement OpImageQuerySizeLod

Tests: dEQP-VK.ycbcr.query.size_lod.*
Tests: dEQP-VK.glsl.texture_functions.query.texturesize.*
Bug: b/129523279
Change-Id: I0a0db0f462d51b6b650ce798eacc66977a3fe896
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30853
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 2008e8e..85cde56 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -868,6 +868,7 @@
 			case spv::OpImageSampleProjDrefExplicitLod:
 			case spv::OpImageFetch:
 			case spv::OpImageQuerySize:
+			case spv::OpImageQuerySizeLod:
 			case spv::OpImageRead:
 			case spv::OpImageTexelPointer:
 			case spv::OpGroupNonUniformElect:
@@ -2441,6 +2442,9 @@
 		case spv::OpImageQuerySize:
 			return EmitImageQuerySize(insn, state);
 
+		case spv::OpImageQuerySizeLod:
+			return EmitImageQuerySizeLod(insn, state);
+
 		case spv::OpImageRead:
 			return EmitImageRead(insn, state);
 
@@ -4728,49 +4732,93 @@
 
 	SpirvShader::EmitResult SpirvShader::EmitImageQuerySize(InsnIterator insn, EmitState *state) const
 	{
-		auto &resultType = getType(Type::ID(insn.word(1)));
+		auto &resultTy = getType(Type::ID(insn.word(1)));
+		auto resultId = Object::ID(insn.word(2));
 		auto imageId = Object::ID(insn.word(3));
+		auto lodId = Object::ID(0);
+
+		auto &dst = state->routine->createIntermediate(resultId, resultTy.sizeInComponents);
+		GetImageDimensions(state->routine, resultTy, imageId, lodId, dst);
+
+		return EmitResult::Continue;
+	}
+
+	SpirvShader::EmitResult SpirvShader::EmitImageQuerySizeLod(InsnIterator insn, EmitState *state) const
+	{
+		auto &resultTy = getType(Type::ID(insn.word(1)));
+		auto resultId = Object::ID(insn.word(2));
+		auto imageId = Object::ID(insn.word(3));
+		auto lodId = Object::ID(insn.word(4));
+
+		auto &dst = state->routine->createIntermediate(resultId, resultTy.sizeInComponents);
+		GetImageDimensions(state->routine, resultTy, imageId, lodId, dst);
+
+		return EmitResult::Continue;
+	}
+
+	void SpirvShader::GetImageDimensions(SpirvRoutine const *routine, Type const &resultTy, Object::ID imageId, Object::ID lodId, Intermediate &dst) const
+	{
 		auto &image = getObject(imageId);
 		auto &imageType = getType(image.type);
-		Object::ID resultId = insn.word(2);
 
 		ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
 		bool isArrayed = imageType.definition.word(5) != 0;
 		bool isCubeMap = imageType.definition.word(3) == spv::DimCube;
 
 		const DescriptorDecorations &d = descriptorDecorations.at(imageId);
-		auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+		auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
 		auto &bindingLayout = setLayout->getBindingLayout(d.Binding);
 
-		Pointer<Byte> binding = state->routine->getPointer(imageId).base;
+		Pointer<Byte> descriptor = routine->getPointer(imageId).base;
 
-		auto &dst = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
+		Pointer<Int> extent;
+		Int arrayLayers;
 
 		switch (bindingLayout.descriptorType)
 		{
 		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
 		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
 		{
-			Pointer<Byte> desc = binding; // StorageImageDescriptor*
-			Pointer<Int> extent = desc + OFFSET(vk::StorageImageDescriptor, extent); // int[3]*
-			auto dimensions = resultType.sizeInComponents - (isArrayed ? 1 : 0);
-			for (uint32_t i = 0; i < dimensions; i++)
-			{
-				dst.move(i, SIMD::Int(extent[i]));
-			}
-			if (isArrayed)
-			{
-				auto arrayLayers = *Pointer<Int>(desc + OFFSET(vk::StorageImageDescriptor, arrayLayers)); // uint32_t
-				auto numElements = isCubeMap ? arrayLayers / 6 : arrayLayers;
-				dst.move(dimensions, SIMD::Int(numElements));
-			}
+			extent = descriptor + OFFSET(vk::StorageImageDescriptor, extent); // int[3]*
+			arrayLayers = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, arrayLayers)); // uint32_t
+			break;
+		}
+		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+		{
+			extent = descriptor + OFFSET(vk::SampledImageDescriptor, extent); // int[3]*
+			arrayLayers = *Pointer<Int>(descriptor + OFFSET(vk::SampledImageDescriptor, arrayLayers)); // uint32_t
 			break;
 		}
 		default:
 			UNREACHABLE("Image descriptorType: %d", int(bindingLayout.descriptorType));
 		}
 
-		return EmitResult::Continue;
+		auto dimensions = resultTy.sizeInComponents - (isArrayed ? 1 : 0);
+		std::vector<Int> out;
+		if (lodId != 0)
+		{
+			auto lodVal = GenericValue(this, routine, lodId);
+			ASSERT(getType(lodVal.type).sizeInComponents == 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));
+			}
+		}
+		else
+		{
+			for (uint32_t i = 0; i < dimensions; i++)
+			{
+				dst.move(i, SIMD::Int(extent[i]));
+			}
+		}
+
+		if (isArrayed)
+		{
+			auto numElements = isCubeMap ? (arrayLayers / 6) : RValue<Int>(arrayLayers);
+			dst.move(dimensions, SIMD::Int(numElements));
+		}
 	}
 
 	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
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 9b2b045..8e3fb76 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -925,6 +925,7 @@
 		EmitResult EmitImageFetch(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageSample(ImageInstruction instruction, InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageQuerySize(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitImageQuerySizeLod(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;
@@ -934,7 +935,8 @@
 		EmitResult EmitCopyMemory(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitGroupNonUniform(InsnIterator insn, EmitState *state) const;
 
-		SIMD::Pointer GetTexelAddress(SpirvRoutine const * routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId, bool useStencilAspect) const;
+		void GetImageDimensions(SpirvRoutine const *routine, Type const &resultTy, Object::ID imageId, Object::ID lodId, Intermediate &dst) const;
+		SIMD::Pointer GetTexelAddress(SpirvRoutine const *routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId, bool useStencilAspect) const;
 		spv::Scope GetScope(Object::ID id) const;
 
 		// OpcodeName() returns the name of the opcode op.
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index e0b9346..8116aba 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -320,6 +320,8 @@
 			}
 
 			imageSampler[i].imageView = imageView;
+			imageSampler[i].extent = imageView->getMipLevelExtent(0);
+			imageSampler[i].arrayLayers = imageView->getSubresourceRange().layerCount;
 
 			auto &subresourceRange = imageView->getSubresourceRange();
 			int baseLevel = subresourceRange.baseMipLevel;
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 4a1a444..22b0e0f 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -36,6 +36,8 @@
 
 	const vk::ImageView *imageView;
 	alignas(16) sw::Texture texture;
+	VkExtent3D extent; // Of base mip-level.
+	int arrayLayers;
 };
 
 struct alignas(16) StorageImageDescriptor