Collect input/output interfaces

I'm still on the fence about leaving complex input/output definitions
intact. However, this provides enough support to walk such structures
all the way down to the leaves and determine exactly which components
are in use, what their types are, and what the interpolation qualifiers
etc should be.

Bug: b/120799499

Change-Id: Ia1e46a571126088e29bfdab6bc5ece0343e294e4
Reviewed-on: https://swiftshader-review.googlesource.com/c/23176
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Device/Config.hpp b/src/Device/Config.hpp
index 5d99047..d3b4889 100644
--- a/src/Device/Config.hpp
+++ b/src/Device/Config.hpp
@@ -97,6 +97,7 @@
 		MAX_TEXTURE_LOD = MIPMAP_LEVELS - 2,   // Trilinear accesses lod+1
 		RENDERTARGETS = 8,
 		NUM_TEMPORARY_REGISTERS = 4096,
+		MAX_INTERFACE_COMPONENTS = 32 * 4,
 	};
 }
 
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 8ae2d8d..641ac81 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -22,11 +22,16 @@
 {
 	volatile int SpirvShader::serialCounter = 1;    // Start at 1, 0 is invalid shader.
 
-	SpirvShader::SpirvShader(InsnStore const &insns) : insns{insns}, serialID{serialCounter++}, modes{}
+	SpirvShader::SpirvShader(InsnStore const &insns)
+			: insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
+			  outputs{MAX_INTERFACE_COMPONENTS},
+			  serialID{serialCounter++}, modes{}
 	{
 		// Simplifying assumptions (to be satisfied by earlier transformations)
 		// - There is exactly one extrypoint in the module, and it's the one we want
-		// - Input / Output interface blocks, builtin or otherwise, have been split.
+		// - Builtin interface blocks have been split. [Splitting user-defined interface blocks
+		//   without changing layout is impossible in the general case because splitting an array
+		//   of structs produces a weirdly-strided array, which SPIRV can't represent.
 		// - The only input/output OpVariables present are those used by the entrypoint
 
 		for (auto insn : *this)
@@ -129,14 +134,28 @@
 				object.sizeInComponents = defs[typeId].sizeInComponents;
 
 				// Register builtins
+
+				// TODO: detect the builtin block!
 				auto &d = decorations[resultId];
-				if (d.HasBuiltIn && storageClass == spv::StorageClassInput)
+				if (storageClass == spv::StorageClassInput)
 				{
-					inputBuiltins[d.BuiltIn] = resultId;
+					if (d.HasBuiltIn)
+					{
+						inputBuiltins[d.BuiltIn] = resultId;
+					} else
+					{
+						PopulateInterface(&inputs, resultId);
+					}
 				}
-				if (d.HasBuiltIn && storageClass == spv::StorageClassOutput)
+				if (storageClass == spv::StorageClassOutput)
 				{
-					outputBuiltins[d.BuiltIn] = resultId;
+					if (d.HasBuiltIn)
+					{
+						outputBuiltins[d.BuiltIn] = resultId;
+					} else
+					{
+						PopulateInterface(&outputs, resultId);
+					}
 				}
 				break;
 			}
@@ -236,6 +255,96 @@
 		}
 	}
 
+	void SpirvShader::PopulateInterfaceSlot(std::vector<InterfaceComponent> *iface, Decorations const &d, AttribType type)
+	{
+		// Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
+		auto scalarSlot = (d.Location << 2) | d.Component;
+
+		auto &slot = (*iface)[scalarSlot];
+		slot.Type = type;
+		slot.Flat = d.Flat;
+		slot.NoPerspective = d.NoPerspective;
+		slot.Centroid = d.Centroid;
+	}
+
+	int SpirvShader::PopulateInterfaceInner(std::vector<InterfaceComponent> *iface, uint32_t id, Decorations d)
+	{
+		// Recursively walks variable definition and its type tree, taking into account
+		// any explicit Location or Component decorations encountered; where explicit
+		// Locations or Components are not specified, assigns them sequentially.
+		// Collected decorations are carried down toward the leaves and across
+		// siblings; Effect of decorations intentionally does not flow back up the tree.
+		//
+		// Returns the next available location.
+
+		// This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
+
+		auto const it = decorations.find(id);
+		if (it != decorations.end())
+		{
+			d.Apply(it->second);
+		}
+
+		auto const &obj = defs[id];
+		switch (obj.definition.opcode())
+		{
+		case spv::OpVariable:
+			return PopulateInterfaceInner(iface, obj.definition.word(1), d);
+		case spv::OpTypePointer:
+			return PopulateInterfaceInner(iface, obj.definition.word(3), d);
+		case spv::OpTypeMatrix:
+			for (auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
+			{
+				// consumes same components of N consecutive locations
+				PopulateInterfaceInner(iface, obj.definition.word(2), d);
+			}
+			return d.Location;
+		case spv::OpTypeVector:
+			for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
+			{
+				// consumes N consecutive components in the same location
+				PopulateInterfaceInner(iface, obj.definition.word(2), d);
+			}
+			return d.Location + 1;
+		case spv::OpTypeFloat:
+			PopulateInterfaceSlot(iface, d, ATTRIBTYPE_FLOAT);
+			return d.Location + 1;
+		case spv::OpTypeInt:
+			PopulateInterfaceSlot(iface, d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
+			return d.Location + 1;
+		case spv::OpTypeBool:
+			PopulateInterfaceSlot(iface, d, ATTRIBTYPE_UINT);
+			return d.Location + 1;
+		case spv::OpTypeStruct:
+		{
+			auto const memberDecorationsIt = memberDecorations.find(id);
+			// iterate over members, which may themselves have Location/Component decorations
+			for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
+			{
+				// Apply any member decorations for this member to the carried state.
+				if (memberDecorationsIt != memberDecorations.end() && i < memberDecorationsIt->second.size())
+				{
+					d.Apply(memberDecorationsIt->second[i]);
+				}
+				d.Location = PopulateInterfaceInner(iface, obj.definition.word(i + 2), d);
+				d.Component = 0;    // Implicit locations always have component=0
+			}
+			return d.Location;
+		}
+			// TODO: array
+		default:
+			// Intentionally partial; most opcodes do not participate in type hierarchies
+			return 0;
+		}
+	}
+
+	void SpirvShader::PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id)
+	{
+		// Walk a variable definition and populate the interface from it.
+		Decorations d{};
+		PopulateInterfaceInner(iface, id, d);
+	}
+
 	void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
 	{
 		switch (decoration)
@@ -256,7 +365,7 @@
 			Flat = true;
 			break;
 		case spv::DecorationNoPerspective:
-			Noperspective = true;
+			NoPerspective = true;
 			break;
 		case spv::DecorationCentroid:
 			Centroid = true;
@@ -295,7 +404,7 @@
 		}
 
 		Flat |= src.Flat;
-		Noperspective |= src.Noperspective;
+		NoPerspective |= src.NoPerspective;
 		Centroid |= src.Centroid;
 		Block |= src.Block;
 		BufferBlock |= src.BufferBlock;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index ac53739..4d5e453 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -163,14 +163,14 @@
 			bool HasBuiltIn : 1;
 			bool Flat : 1;
 			bool Centroid : 1;
-			bool Noperspective : 1;
+			bool NoPerspective : 1;
 			bool Block : 1;
 			bool BufferBlock : 1;
 
 			Decorations()
 					: Location{-1}, Component{0}, BuiltIn{}, HasLocation{false}, HasComponent{false}, HasBuiltIn{false},
 					  Flat{false},
-					  Centroid{false}, Noperspective{false}, Block{false},
+					  Centroid{false}, NoPerspective{false}, Block{false},
 					  BufferBlock{false}
 			{
 			}
@@ -185,6 +185,22 @@
 		std::unordered_map<uint32_t, Decorations> decorations;
 		std::unordered_map<uint32_t, std::vector<Decorations>> memberDecorations;
 
+		struct InterfaceComponent
+		{
+			AttribType Type;
+			bool Flat : 1;
+			bool Centroid : 1;
+			bool NoPerspective : 1;
+
+			InterfaceComponent()
+					: Type{ATTRIBTYPE_UNUSED}, Flat{false}, Centroid{false}, NoPerspective{false}
+			{
+			}
+		};
+
+		std::vector<InterfaceComponent> inputs;
+		std::vector<InterfaceComponent> outputs;
+
 	private:
 		const int serialID;
 		static volatile int serialCounter;
@@ -196,6 +212,12 @@
 		void ProcessExecutionMode(InsnIterator it);
 
 		uint32_t ComputeTypeSize(InsnIterator insn);
+
+		void PopulateInterfaceSlot(std::vector<InterfaceComponent> *iface, Decorations const &d, AttribType type);
+
+		int PopulateInterfaceInner(std::vector<InterfaceComponent> *iface, uint32_t id, Decorations d);
+
+		void PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id);
 	};
 }