Identify image views based on their state

It is typical for apps to have many image views (and buffer views) with
the same parameters. This change avoids generating new sampling routines
for image views with identical state.

The vk::Identifier class compresses the state into a 32-bit value that
is used as part of the key for the sampling routine cache.

Bug: b/151235334
Change-Id: I00fc19a91639803273d0f7d6b9fed9fc5b481898
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/42388
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Vulkan/VkBufferView.cpp b/src/Vulkan/VkBufferView.cpp
index a8b49a0..3e048f1 100644
--- a/src/Vulkan/VkBufferView.cpp
+++ b/src/Vulkan/VkBufferView.cpp
@@ -19,7 +19,8 @@
 namespace vk {
 
 BufferView::BufferView(const VkBufferViewCreateInfo *pCreateInfo, void *mem)
-    : buffer(vk::Cast(pCreateInfo->buffer))
+    : id(pCreateInfo->format)
+    , buffer(vk::Cast(pCreateInfo->buffer))
     , format(pCreateInfo->format)
     , offset(pCreateInfo->offset)
 {
@@ -38,4 +39,4 @@
 	return buffer->getOffsetPointer(offset);
 }
 
-}  // namespace vk
\ No newline at end of file
+}  // namespace vk
diff --git a/src/Vulkan/VkBufferView.hpp b/src/Vulkan/VkBufferView.hpp
index 98b68cc..aa13fb5 100644
--- a/src/Vulkan/VkBufferView.hpp
+++ b/src/Vulkan/VkBufferView.hpp
@@ -38,7 +38,8 @@
 	uint32_t getRangeInBytes() const { return static_cast<uint32_t>(range); }
 	VkFormat getFormat() const { return format; }
 
-	const uint32_t id = ImageView::nextID++;  // ID space for sampling function cache, shared with imageviews
+	const Identifier id;
+
 private:
 	Buffer *buffer;
 	VkFormat format;
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index fc0c8bd..d9fbaf6 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -14,7 +14,9 @@
 
 #include "VkImageView.hpp"
 #include "VkImage.hpp"
-#include <System/Math.hpp>
+#include "System/Math.hpp"
+
+#include <climits>
 
 namespace {
 
@@ -54,7 +56,28 @@
 
 namespace vk {
 
-std::atomic<uint32_t> ImageView::nextID(1);
+Identifier::Identifier(const Image *image, VkImageViewType type, VkFormat fmt, VkComponentMapping mapping)
+{
+	imageViewType = type;
+	format = Format::mapTo8bit(fmt);
+	r = mapping.r;
+	g = mapping.g;
+	b = mapping.b;
+	a = mapping.a;
+
+	// TODO(b/152224843): eliminate
+	auto extent = image->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+	large = (extent.width > SHRT_MAX) ||
+	        (extent.height > SHRT_MAX) ||
+	        (extent.depth > SHRT_MAX);
+}
+
+Identifier::Identifier(VkFormat fmt)
+{
+	static_assert(VK_IMAGE_VIEW_TYPE_END_RANGE == 6, "VkImageViewType does not allow using 7 to indicate buffer view");
+	imageViewType = 7;  // Still fits in 3-bit field
+	format = Format::mapTo8bit(fmt);
+}
 
 ImageView::ImageView(const VkImageViewCreateInfo *pCreateInfo, void *mem, const vk::SamplerYcbcrConversion *ycbcrConversion)
     : image(vk::Cast(pCreateInfo->image))
@@ -63,6 +86,7 @@
     , components(ResolveComponentMapping(pCreateInfo->components, format))
     , subresourceRange(ResolveRemainingLevelsLayers(pCreateInfo->subresourceRange, image))
     , ycbcrConversion(ycbcrConversion)
+    , id(image, viewType, format, components)
 {
 }
 
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 65dca48..08ed4af 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -27,6 +27,36 @@
 
 class SamplerYcbcrConversion;
 
+// Uniquely identifies state used by sampling routine generation.
+// ID space shared by image views and buffer views.
+union Identifier
+{
+	// Image view identifier
+	Identifier(const Image *image, VkImageViewType type, VkFormat format, VkComponentMapping mapping);
+
+	// Buffer view identifier
+	Identifier(VkFormat format);
+
+	operator uint32_t() const
+	{
+		static_assert(sizeof(Identifier) == sizeof(uint32_t), "Identifier must be 32-bit");
+		return id;
+	}
+
+	uint32_t id = 0;
+
+	struct
+	{
+		uint32_t imageViewType : 3;
+		uint32_t format : 8;
+		uint32_t r : 3;
+		uint32_t g : 3;
+		uint32_t b : 3;
+		uint32_t a : 3;
+		uint32_t large : 1;  // Has dimension larger than SHRT_MAX (see b/133429305).
+	};
+};
+
 class ImageView : public Object<ImageView, VkImageView>
 {
 public:
@@ -82,22 +112,20 @@
 	const VkImageSubresourceRange &getSubresourceRange() const { return subresourceRange; }
 	size_t getImageSizeInBytes() const { return image->getMemoryRequirements().size; }
 
-	const uint32_t id = nextID++;
-
 private:
-	static std::atomic<uint32_t> nextID;
-	friend class BufferView;  // ImageView/BufferView share the ID space above.
-
 	bool imageTypesMatch(VkImageType imageType) const;
 	const Image *getImage(Usage usage) const;
 
 	Image *const image = nullptr;
 	const VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D;
-	const Format format;
+	const Format format = VK_FORMAT_UNDEFINED;
 	const VkComponentMapping components = {};
 	const VkImageSubresourceRange subresourceRange = {};
 
 	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
+
+public:
+	const Identifier id;
 };
 
 // TODO(b/132437008): Also used by SamplerYcbcrConversion. Move somewhere centrally?