Uniform block register allocation

Uniform blocks members, when unnamed blocks are used, are
used in glsl only with the member's name, without any
reference to the block itself. When this happens, we still
need the whole block to be allocated as one contiguous
structure in the registers. To do that, whenever a member
of an unnamed block is first used, the whole block is
allocated. Also, whenever any member of an unnamed block
attempts to allocate a register, it first verifies if the
parent block has already been allocated. So this means that
both allocation and lookup must be done through the parent
block when dealing with unnamed block members.

Change-Id: Ib11eaa1ee052d32252f32997eb5f650a9765533d
Reviewed-on: https://swiftshader-review.googlesource.com/5059
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp
index 9ca21ad..568fc8e 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -2792,14 +2792,28 @@
 

 		if(symbol || block)

 		{

-			int index = lookup(uniforms, uniform);

+			TInterfaceBlock* parentBlock = type.getInterfaceBlock();

+			bool isBlockMember = (!block && parentBlock);

+			int index = isBlockMember ? lookup(uniforms, parentBlock) : lookup(uniforms, uniform);

 

-			if(index == -1)

+			if(index == -1 || isBlockMember)

 			{

-				index = allocate(uniforms, uniform);

-				const TString &name = symbol ? symbol->getSymbol() : block->name();

+				if(index == -1)

+				{

+					index = allocate(uniforms, uniform);

+				}

 

-				declareUniform(type, name, index);

+				// Verify if the current uniform is a member of an already declared block

+				const TString &name = symbol ? symbol->getSymbol() : block->name();

+				int blockMemberIndex = blockMemberLookup(type, name, index);

+				if(blockMemberIndex == -1)

+				{

+					declareUniform(type, name, index);

+				}

+				else

+				{

+					index = blockMemberIndex;

+				}

 			}

 

 			return index;

@@ -2953,13 +2967,25 @@
 		return -1;

 	}

 

+	int OutputASM::lookup(VariableArray &list, TInterfaceBlock *block)

+	{

+		for(unsigned int i = 0; i < list.size(); i++)

+		{

+			if(list[i] && (list[i]->getType().getInterfaceBlock() == block))

+			{

+				return i;   // Pointer match

+			}

+		}

+		return -1;

+	}

+

 	int OutputASM::allocate(VariableArray &list, TIntermTyped *variable)

 	{

 		int index = lookup(list, variable);

 

 		if(index == -1)

 		{

-			unsigned int registerCount = variable->totalRegisterCount();

+			unsigned int registerCount = variable->blockRegisterCount();

 

 			for(unsigned int i = 0; i < list.size(); i++)

 			{

@@ -3007,14 +3033,54 @@
 		}

 	}

 

-	int OutputASM::declareUniform(const TType &type, const TString &name, int registerIndex, int blockId, BlockLayoutEncoder* encoder)

+	int OutputASM::blockMemberLookup(const TType &type, const TString &name, int registerIndex)

+	{

+		const TInterfaceBlock *block = type.getInterfaceBlock();

+

+		if(block)

+		{

+			ActiveUniformBlocks &activeUniformBlocks = shaderObject->activeUniformBlocks;

+			const TFieldList& fields = block->fields();

+			const TString &blockName = block->name();

+			int fieldRegisterIndex = registerIndex;

+

+			if(!type.isInterfaceBlock())

+			{

+				// This is a uniform that's part of a block, let's see if the block is already defined

+				for(size_t i = 0; i < activeUniformBlocks.size(); ++i)

+				{

+					if(activeUniformBlocks[i].name == blockName.c_str())

+					{

+						// The block is already defined, find the register for the current uniform and return it

+						for(size_t j = 0; j < fields.size(); j++)

+						{

+							const TString &fieldName = fields[j]->name();

+							if(fieldName == name)

+							{

+								return fieldRegisterIndex;

+							}

+

+							fieldRegisterIndex += fields[j]->type()->totalRegisterCount();

+						}

+

+						ASSERT(false);

+						return fieldRegisterIndex;

+					}

+				}

+			}

+		}

+

+		return -1;

+	}

+

+	void OutputASM::declareUniform(const TType &type, const TString &name, int registerIndex, int blockId, BlockLayoutEncoder* encoder)

 	{

 		const TStructure *structure = type.getStruct();

 		const TInterfaceBlock *block = (type.isInterfaceBlock() || (blockId == -1)) ? type.getInterfaceBlock() : nullptr;

-		ActiveUniforms &activeUniforms = shaderObject->activeUniforms;

 

 		if(!structure && !block)

 		{

+			ActiveUniforms &activeUniforms = shaderObject->activeUniforms;

 			const BlockMemberInfo blockInfo = encoder ? encoder->encodeType(type) : BlockMemberInfo::getDefaultBlockInfo();

 			if(blockId >= 0)

 			{

@@ -3040,31 +3106,6 @@
 			int fieldRegisterIndex = registerIndex;

 			bool isUniformBlockMember = !type.isInterfaceBlock() && (blockId == -1);

 

-			if(isUniformBlockMember)

-			{

-				// This is a uniform that's part of a block, let's see if the block is already defined

-				for(size_t i = 0; i < activeUniformBlocks.size(); ++i)

-				{

-					if(activeUniformBlocks[i].name == blockName.c_str())

-					{

-						// The block is already defined, find the register for the current uniform and return it

-						for(size_t j = 0; j < fields.size(); j++)

-						{

-							const TString &fieldName = fields[j]->name();

-							if(fieldName == name)

-							{

-								return fieldRegisterIndex;

-							}

-

-							fieldRegisterIndex += fields[j]->type()->totalRegisterCount();

-						}

-

-						ASSERT(false);

-						return fieldRegisterIndex;

-					}

-				}

-			}

-

 			blockId = activeUniformBlocks.size();

 			bool isRowMajor = block->matrixPacking() == EmpRowMajor;

 			activeUniformBlocks.push_back(UniformBlock(blockName.c_str(), 0, block->arraySize(),

@@ -3139,8 +3180,6 @@
 				}

 			}

 		}

-

-		return registerIndex;

 	}

 

 	GLenum OutputASM::glVariableType(const TType &type)

diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h
index b0d3e24..83d2e62 100644
--- a/src/OpenGL/compiler/OutputASM.h
+++ b/src/OpenGL/compiler/OutputASM.h
@@ -290,10 +290,12 @@
 		typedef std::vector<TIntermTyped*> VariableArray;

 

 		int lookup(VariableArray &list, TIntermTyped *variable);

+		int lookup(VariableArray &list, TInterfaceBlock *block);

+		int blockMemberLookup(const TType &type, const TString &name, int registerIndex);

 		int allocate(VariableArray &list, TIntermTyped *variable);

 		void free(VariableArray &list, TIntermTyped *variable);

 

-		int declareUniform(const TType &type, const TString &name, int registerIndex, int blockId = -1, BlockLayoutEncoder* encoder = nullptr);

+		void declareUniform(const TType &type, const TString &name, int registerIndex, int blockId = -1, BlockLayoutEncoder* encoder = nullptr);

 		GLenum glVariableType(const TType &type);

 		GLenum glVariablePrecision(const TType &type);

 

diff --git a/src/OpenGL/compiler/Types.h b/src/OpenGL/compiler/Types.h
index 3e760f3..f7138e7 100644
--- a/src/OpenGL/compiler/Types.h
+++ b/src/OpenGL/compiler/Types.h
@@ -333,6 +333,24 @@
 		}
 	}
 
+	int blockRegisterCount() const
+	{
+		// If this TType object is a block member, return the register count of the parent block
+		// Otherwise, return the register count of the current TType object
+		if(interfaceBlock && !isInterfaceBlock())
+		{
+			int registerCount = 0;
+			const TFieldList& fieldList = interfaceBlock->fields();
+			for(size_t i = 0; i < fieldList.size(); i++)
+			{
+				const TType &fieldType = *(fieldList[i]->type());
+				registerCount += fieldType.totalRegisterCount();
+			}
+			return registerCount;
+		}
+		return totalRegisterCount();
+	}
+
 	int totalRegisterCount() const
 	{
 		if(array)
diff --git a/src/OpenGL/compiler/intermediate.h b/src/OpenGL/compiler/intermediate.h
index 8b72d30..8228787 100644
--- a/src/OpenGL/compiler/intermediate.h
+++ b/src/OpenGL/compiler/intermediate.h
@@ -320,6 +320,7 @@
     TString getCompleteString() const { return type.getCompleteString(); }
 
 	int totalRegisterCount() const { return type.totalRegisterCount(); }
+	int blockRegisterCount() const { return type.blockRegisterCount(); }
 	int elementRegisterCount() const { return type.elementRegisterCount(); }
 	int registerSize() const { return type.registerSize(); }
 	int getArraySize() const { return type.getArraySize(); }