Fixed sampler within struct uniform used as function argument

The sampler was simply not declared in that case, as if the uniform
was unused in the shader, so making sure structures containing
samplers call samplerRegister solves this issue. It was working
properly if the sampler was used anywhere else in the shader, but
failed when the only use in the shader was being passed as an
argument to a function through a containing structure.

Added a unit test since this isn't covered by dEQP.

Fixes 2 WebGL tests:
conformance/glsl/bugs/sampler-array-struct-function-arg.html
conformance/glsl/bugs/sampler-struct-function-arg.html

Change-Id: I81767d7c6415de7aefefecffcc66265d944a94ab
Reviewed-on: https://swiftshader-review.googlesource.com/18628
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp
index 8477af2..b6b0eef 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -2859,6 +2859,10 @@
 		{
 			return samplerRegister(operand);
 		}
+		else if(operand->getType().totalSamplerRegisterCount() > 0) // Struct containing a sampler
+		{
+			samplerRegister(operand); // Make sure the sampler is declared
+		}
 
 		switch(operand->getQualifier())
 		{
diff --git a/tests/unittests/unittests.cpp b/tests/unittests/unittests.cpp
index 99cbb61..1418e43 100644
--- a/tests/unittests/unittests.cpp
+++ b/tests/unittests/unittests.cpp
@@ -233,6 +233,10 @@
 		glLinkProgram(ph.program);
 		EXPECT_GLENUM_EQ(GL_NONE, glGetError());
 
+		GLint linkStatus = 0;
+		glGetProgramiv(ph.program, GL_LINK_STATUS, &linkStatus);
+		EXPECT_NE(linkStatus, 0);
+
 		return ph;
 	}
 
@@ -243,7 +247,7 @@
 		glDeleteProgram(ph.program);
 	}
 
-	void drawQuad(GLuint program)
+	void drawQuad(GLuint program, const char* textureName)
 	{
 		GLint prevProgram = 0;
 		glGetIntegerv(GL_CURRENT_PROGRAM, &prevProgram);
@@ -254,7 +258,7 @@
 		GLint posLoc = glGetAttribLocation(program, "position");
 		EXPECT_GLENUM_EQ(GL_NONE, glGetError());
 
-		GLint location = glGetUniformLocation(program, "tex");
+		GLint location = glGetUniformLocation(program, textureName);
 		ASSERT_NE(-1, location);
 		glUniform1i(location, 0);
 
@@ -306,6 +310,64 @@
 	Uninitialize();
 }
 
+// Test sampling from a sampler in a struct as a function argument
+TEST_F(SwiftShaderTest, SamplerArrayInStructArrayAsFunctionArg)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_2D, tex);
+	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
+
+	unsigned char green[4] = { 0, 255, 0, 255 };
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, green);
+	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
+
+	const std::string vs =
+		"#version 300 es\n"
+		"in vec4 position;\n"
+		"void main()\n"
+		"{\n"
+		"	gl_Position = vec4(position.xy, 0.0, 1.0);\n"
+		"}\n";
+
+	const std::string fs =
+		"#version 300 es\n"
+		"precision mediump float;\n"
+		"struct SamplerStruct{ sampler2D tex[2]; };\n"
+		"vec4 doSample(in SamplerStruct s[2])\n"
+		"{\n"
+		"	return texture(s[1].tex[1], vec2(0.0));\n"
+		"}\n"
+		"uniform SamplerStruct samplerStruct[2];\n"
+		"out vec4 fragColor;\n"
+		"void main()\n"
+		"{\n"
+		"	fragColor = doSample(samplerStruct);\n"
+		"}\n";
+
+	const ProgramHandles ph = createProgram(vs, fs);
+
+	glUseProgram(ph.program);
+	GLint location = glGetUniformLocation(ph.program, "samplerStruct[1].tex[1]");
+	ASSERT_NE(-1, location);
+	glUniform1i(location, 0);
+
+	glClearColor(0.0, 0.0, 0.0, 0.0);
+	glClear(GL_COLOR_BUFFER_BIT);
+	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
+
+	drawQuad(ph.program, "samplerStruct[1].tex[1]");
+
+	deleteProgram(ph);
+
+	compareColor(green);
+
+	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
+
+	Uninitialize();
+}
+
 // Note: GL_ARB_texture_rectangle is part of gl2extchromium.h in the Chromium repo
 // GL_ARB_texture_rectangle
 #ifndef GL_ARB_texture_rectangle
@@ -558,7 +620,7 @@
 	glClear(GL_COLOR_BUFFER_BIT);
 	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
 
-	drawQuad(ph.program);
+	drawQuad(ph.program, "tex");
 
 	deleteProgram(ph);
 
@@ -612,7 +674,7 @@
 	glClear(GL_COLOR_BUFFER_BIT);
 	EXPECT_GLENUM_EQ(GL_NONE, glGetError());
 
-	drawQuad(ph.program);
+	drawQuad(ph.program, "tex");
 
 	deleteProgram(ph);
 
@@ -976,7 +1038,7 @@
 
 		const ProgramHandles ph = createProgram(vs, fs);
 
-		drawQuad(ph.program);
+		drawQuad(ph.program, "tex");
 
 		deleteProgram(ph);