Only perform LOD computation when it is necessary

If an image has a single mip level, and filtering isn't LOD dependent,
as is the case when using FILTER_MIN_POINT_MAG_LINEAR or
FILTER_MIN_LINEAR_MAG_POINT, LOD computation is not necessary in
order to perform image sampling and can be bypassed entirely.

Bug: b/179889245
Change-Id: Ic69fe0c45211ccbb66c88c502c2dba1c50630aa7
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/52688
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Commit-Queue: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp
index 646d875..3d5521d 100644
--- a/src/Pipeline/SamplerCore.cpp
+++ b/src/Pipeline/SamplerCore.cpp
@@ -54,7 +54,15 @@
 		w = As<Float4>(face);
 	}
 
-	if(function == Implicit || function == Bias || function == Grad || function == Query)
+	bool singleMipLevel = (state.minLod == state.maxLod) && (function != Query) && (function != Fetch);
+
+	// We can't skip the LOD computation for LOD query, where we have to return the proper value
+	if(singleMipLevel)
+	{
+		// Skip costly LOD computation if there's only 1 possible outcome
+		lod = state.minLod;
+	}
+	else if(function == Implicit || function == Bias || function == Grad || function == Query)
 	{
 		if(state.is1D())
 		{
@@ -108,8 +116,11 @@
 			c.y = Float4(lod);  // Unclamped LOD.
 		}
 
-		lod = Max(lod, state.minLod);
-		lod = Min(lod, state.maxLod);
+		if (!singleMipLevel)
+		{
+			lod = Max(lod, state.minLod);
+			lod = Min(lod, state.maxLod);
+		}
 
 		if(function == Query)
 		{
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index 44740b8..6aedc33 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -78,6 +78,20 @@
 			samplerState.maxAnisotropy = vkSamplerState->maxAnisotropy;
 			samplerState.minLod = vkSamplerState->minLod;
 			samplerState.maxLod = vkSamplerState->maxLod;
+
+			// If there's a single mip level and filtering doesn't depend on the LOD level,
+			// the sampler will need to compute the LOD to produce the proper result.
+			// Otherwise, it can be ignored.
+			// We can skip the LOD computation for all modes, except LOD query,
+			// where we have to return the proper value even if nothing else requires it.
+			if(imageViewState.singleMipLevel &&
+			   (samplerState.textureFilter != FILTER_MIN_POINT_MAG_LINEAR) &&
+			   (samplerState.textureFilter != FILTER_MIN_LINEAR_MAG_POINT) &&
+			   (samplerMethod != Query))
+			{
+				samplerState.minLod = 0.0f;
+				samplerState.maxLod = 0.0f;
+			}
 		}
 		else
 		{
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index 0aa34f6..7b962ab 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -87,14 +87,14 @@
 	const Image *sampledImage = image->getSampledImage(viewFormat);
 
 	vk::Format samplingFormat = (image == sampledImage) ? viewFormat : sampledImage->getFormat().getAspectFormat(subresource.aspectMask);
-	pack({ pCreateInfo->viewType, samplingFormat, ResolveComponentMapping(pCreateInfo->components, viewFormat) });
+	pack({ pCreateInfo->viewType, samplingFormat, ResolveComponentMapping(pCreateInfo->components, viewFormat), subresource.levelCount <= 1u });
 }
 
 Identifier::Identifier(VkFormat bufferFormat)
 {
 	static_assert(vk::VK_IMAGE_VIEW_TYPE_END_RANGE == 6, "VkImageViewType does not allow using 7 to indicate buffer view");
 	constexpr VkComponentMapping identityMapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
-	pack({ VK_IMAGE_VIEW_TYPE_1D, bufferFormat, ResolveComponentMapping(identityMapping, bufferFormat) });
+	pack({ VK_IMAGE_VIEW_TYPE_1D, bufferFormat, ResolveComponentMapping(identityMapping, bufferFormat), true });
 }
 
 void Identifier::pack(const State &state)
@@ -105,6 +105,7 @@
 	g = static_cast<uint32_t>(state.mapping.g);
 	b = static_cast<uint32_t>(state.mapping.b);
 	a = static_cast<uint32_t>(state.mapping.a);
+	singleMipLevel = state.singleMipLevel;
 }
 
 Identifier::State Identifier::getState() const
@@ -114,7 +115,8 @@
 		     { static_cast<VkComponentSwizzle>(r),
 		       static_cast<VkComponentSwizzle>(g),
 		       static_cast<VkComponentSwizzle>(b),
-		       static_cast<VkComponentSwizzle>(a) } };
+		       static_cast<VkComponentSwizzle>(a) },
+		     static_cast<bool>(singleMipLevel) };
 }
 
 ImageView::ImageView(const VkImageViewCreateInfo *pCreateInfo, void *mem, const vk::SamplerYcbcrConversion *ycbcrConversion)
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 7efab9c..86bfd38 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -53,6 +53,7 @@
 		VkImageViewType imageViewType;
 		VkFormat format;
 		VkComponentMapping mapping;
+		bool singleMipLevel;
 	};
 	State getState() const;
 
@@ -68,6 +69,7 @@
 		uint32_t g : 3;
 		uint32_t b : 3;
 		uint32_t a : 3;
+		uint32_t singleMipLevel : 1;
 	};
 
 	uint32_t id = 0;