Fix indexing of samplers in structures.

Change-Id: I0bde678865902d549d4260bf8f6c1a368a46f4d4
Reviewed-on: https://swiftshader-review.googlesource.com/5191
Tested-by: Nicolas Capens <capn@google.com>
Reviewed-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 cc4bd45..88a8780 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -495,6 +495,11 @@
 		const TType &rightType = right->getType();

 		const TType &resultType = node->getType();

 

+		if(isSamplerRegister(result))

+		{

+			return false;   // Don't traverse, the register index is determined statically

+		}

+

 		switch(node->getOp())

 		{

 		case EOpAssign:

@@ -1758,21 +1763,6 @@
 		return true;

 	}

 

-	bool OutputASM::isSamplerRegister(TIntermTyped *operand)

-	{

-		return operand && isSamplerRegister(operand->getType());

-	}

-

-	bool OutputASM::isSamplerRegister(const TType &type)

-	{

-		// A sampler register's qualifiers can be:

-		// - EvqUniform: The sampler uniform is used as is in the code (default case).

-		// - EvqTemporary: The sampler is indexed. It's still a sampler register.

-		// - EvqIn (and other similar types): The sampler has been passed as a function argument. At this point,

-		//                                    the sampler has been copied and is no longer a sampler register.

-		return IsSampler(type.getBasicType()) && (type.getQualifier() == EvqUniform || type.getQualifier() == EvqTemporary);

-	}

-

 	Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, TIntermNode *src0, TIntermNode *src1, TIntermNode *src2, TIntermNode *src3, TIntermNode *src4)

 	{

 		return emit(op, dst, 0, src0, 0, src1, 0, src2, 0, src3, 0, src4, 0);

@@ -1781,11 +1771,6 @@
 	Instruction *OutputASM::emit(sw::Shader::Opcode op, TIntermTyped *dst, int dstIndex, TIntermNode *src0, int index0, TIntermNode *src1, int index1,

 	                             TIntermNode *src2, int index2, TIntermNode *src3, int index3, TIntermNode *src4, int index4)

 	{

-		if(isSamplerRegister(dst))

-		{

-			op = sw::Shader::OPCODE_NULL;   // Can't assign to a sampler, but this is hit when indexing sampler arrays

-		}

-

 		Instruction *instruction = new Instruction(op);

 

 		if(dst)

@@ -2172,39 +2157,7 @@
 			{

 				parameter.index = registerIndex(arg) + argumentInfo.clampedIndex;

 

-				if(isSamplerRegister(arg))

-				{

-					TIntermBinary *binary = argument->getAsBinaryNode();

-

-					if(binary)

-					{

-						TIntermTyped *left = binary->getLeft();

-						TIntermTyped *right = binary->getRight();

-

-						switch(binary->getOp())

-						{

-						case EOpIndexDirect:

-							parameter.index += right->getAsConstantUnion()->getIConst(0);

-							break;

-						case EOpIndexIndirect:

-							if(left->getArraySize() > 1)

-							{

-								parameter.rel.type = registerType(binary->getRight());

-								parameter.rel.index = registerIndex(binary->getRight());

-								parameter.rel.scale = 1;

-								parameter.rel.deterministic = true;

-							}

-							break;

-						case EOpIndexDirectStruct:

-						case EOpIndexDirectInterfaceBlock:

-							parameter.index += right->getAsConstantUnion()->getIConst(0);

-							break;

-						default:

-							UNREACHABLE(binary->getOp());

-						}

-					}

-				}

-				else if(parameter.bufferIndex != -1)

+				if(parameter.bufferIndex != -1)

 				{

 					int stride = (argumentInfo.typedMemberInfo.matrixStride > 0) ? argumentInfo.typedMemberInfo.matrixStride : argumentInfo.typedMemberInfo.arrayStride;

 					parameter.index = argumentInfo.typedMemberInfo.offset + argumentInfo.clampedIndex * stride;

@@ -2884,30 +2837,57 @@
 

 	int OutputASM::samplerRegister(TIntermTyped *sampler)

 	{

-		ASSERT(IsSampler(sampler->getType().getBasicType()));

+		const TType &type = sampler->getType();

+		ASSERT(IsSampler(type.getBasicType()) || type.isStruct());   // Structures can contain samplers

+

 		TIntermSymbol *symbol = sampler->getAsSymbolNode();

 		TIntermBinary *binary = sampler->getAsBinaryNode();

 

-		if(symbol)

+		if(symbol && type.getQualifier() == EvqUniform)

 		{

 			return samplerRegister(symbol);

 		}

 		else if(binary)

 		{

-			ASSERT(binary->getOp() == EOpIndexDirect || binary->getOp() == EOpIndexIndirect ||

-				   binary->getOp() == EOpIndexDirectStruct || binary->getOp() == EOpIndexDirectInterfaceBlock);

+			TIntermTyped *left = binary->getLeft();

+			TIntermTyped *right = binary->getRight();

+			const TType &leftType = left->getType();

+			int index = right->getAsConstantUnion() ? right->getAsConstantUnion()->getIConst(0) : 0;

 

-			return samplerRegister(binary->getLeft());   // Index added later

+			switch(binary->getOp())

+			{

+			case EOpIndexDirect:

+				ASSERT(left->isArray());

+				return samplerRegister(left) + index * leftType.elementRegisterCount();

+			case EOpIndexDirectStruct:

+				{

+					ASSERT(leftType.isStruct());

+					const TFieldList &fields = leftType.getStruct()->fields();

+					int fieldOffset = 0;

+

+					for(int i = 0; i < index; i++)

+					{

+						fieldOffset += fields[i]->type()->totalRegisterCount();

+					}

+

+					return samplerRegister(left) + fieldOffset;

+				}

+			case EOpIndexDirectInterfaceBlock:   // Interface blocks can't contain samplers

+			case EOpIndexIndirect:               // Indirect indexing produces a temporary, not a sampler register

+			default:

+				UNREACHABLE(binary->getOp());

+				return 0;

+			}

 		}

-		else UNREACHABLE(0);

 

-		return 0;

+		UNREACHABLE(0);

+		return 0;   // Not a sampler register

 	}

 

 	int OutputASM::samplerRegister(TIntermSymbol *sampler)

 	{

 		const TType &type = sampler->getType();

-		ASSERT(IsSampler(type.getBasicType()) || type.getStruct());   // Structures can contain samplers

+		ASSERT(IsSampler(type.getBasicType()) || type.isStruct());   // Structures can contain samplers

 

 		int index = lookup(samplers, sampler);

 

@@ -2925,6 +2905,11 @@
 		return index;

 	}

 

+	bool OutputASM::isSamplerRegister(TIntermTyped *operand)

+	{

+		return operand && IsSampler(operand->getBasicType()) && samplerRegister(operand) >= 0;

+	}

+

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

 	{

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

@@ -3110,7 +3095,7 @@
 			int fieldRegisterIndex = encoder ? shaderObject->activeUniformBlocks[blockId].registerIndex + BlockLayoutEncoder::getBlockRegister(blockInfo) : registerIndex;

 			activeUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(),

 			                                 fieldRegisterIndex, blockId, blockInfo));

-			if(isSamplerRegister(type))

+			if(IsSampler(type.getBasicType()))

 			{

 				for(int i = 0; i < type.totalRegisterCount(); i++)

 				{

diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h
index 2329cf5..854c262 100644
--- a/src/OpenGL/compiler/OutputASM.h
+++ b/src/OpenGL/compiler/OutputASM.h
@@ -286,6 +286,7 @@
 		int fragmentOutputRegister(TIntermTyped *fragmentOutput);

 		int samplerRegister(TIntermTyped *sampler);

 		int samplerRegister(TIntermSymbol *sampler);

+		bool isSamplerRegister(TIntermTyped *operand);

 

 		typedef std::vector<TIntermTyped*> VariableArray;

 

@@ -302,8 +303,6 @@
 		static int dim(TIntermNode *v);

 		static int dim2(TIntermNode *m);

 		static unsigned int loopCount(TIntermLoop *node);

-		static bool isSamplerRegister(TIntermTyped *operand);

-		static bool isSamplerRegister(const TType &type);

 

 		Shader *const shaderObject;

 		sw::Shader *shader;