Reduce and verify fragment output PIQ test draw buffer usage.

- Check GL_MAX_DRAW_BUFFERS implementation limit and usage if shader
  build fails.
- Reduce required GL_MAX_DRAW_BUFFERS from 5 to 4 in explicit location
  fragment output array tests.
- Fix various max binding resource usage checks.

Bug: 18094242
Change-Id: I17e091eb1939fd7ec94789be70be5a069970b697
diff --git a/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.cpp b/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.cpp
index ccc0d80..5dca33b 100644
--- a/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.cpp
+++ b/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.cpp
@@ -893,6 +893,35 @@
 	return numComponents;
 }
 
+static int getFragmentOutputMaxLocation (const ProgramInterfaceDefinition::Shader* shader)
+{
+	DE_ASSERT(shader->getType() == glu::SHADERTYPE_FRAGMENT);
+
+	int maxOutputLocation = -1;
+
+	for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx)
+	{
+		if (shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_OUT)
+		{
+			// missing location qualifier means location == 0
+			const int outputLocation 		= (shader->getDefaultBlock().variables[ndx].layout.location == -1)
+												? (0)
+												: (shader->getDefaultBlock().variables[ndx].layout.location);
+
+			// only basic types or arrays of basic types possible
+			DE_ASSERT(!shader->getDefaultBlock().variables[ndx].varType.isStructType());
+
+			const int locationSlotsTaken	= (shader->getDefaultBlock().variables[ndx].varType.isArrayType())
+												? (shader->getDefaultBlock().variables[ndx].varType.getArraySize())
+												: (1);
+
+			maxOutputLocation = de::max(maxOutputLocation, outputLocation + locationSlotsTaken - 1);
+		}
+	}
+
+	return maxOutputLocation;
+}
+
 } // anonymous
 
 std::vector<std::string> getProgramInterfaceBlockMemberResourceList (const glu::InterfaceBlock& interfaceBlock)
@@ -1315,27 +1344,28 @@
 {
 	ProgramInterfaceDefinition::ProgramResourceUsage retVal;
 
-	retVal.uniformBufferMaxBinding				= 0;
+	retVal.uniformBufferMaxBinding				= -1; // max binding is inclusive upper bound. Allow 0 bindings by using negative value
 	retVal.uniformBufferMaxSize					= 0;
 	retVal.numUniformBlocks						= 0;
 	retVal.numCombinedVertexUniformComponents	= 0;
 	retVal.numCombinedFragmentUniformComponents	= 0;
-	retVal.shaderStorageBufferMaxBinding		= 0;
+	retVal.shaderStorageBufferMaxBinding		= -1; // see above
 	retVal.shaderStorageBufferMaxSize			= 0;
 	retVal.numShaderStorageBlocks				= 0;
 	retVal.numVaryingComponents					= 0;
 	retVal.numVaryingVectors					= 0;
 	retVal.numCombinedSamplers					= 0;
-	retVal.atomicCounterBufferMaxBinding		= 0;
+	retVal.atomicCounterBufferMaxBinding		= -1; // see above
 	retVal.atomicCounterBufferMaxSize			= 0;
 	retVal.numAtomicCounterBuffers				= 0;
 	retVal.numAtomicCounters					= 0;
-	retVal.maxImageBinding						= 0;
+	retVal.maxImageBinding						= -1; // see above
 	retVal.numCombinedImages					= 0;
 	retVal.numCombinedOutputResources			= 0;
 	retVal.numXFBInterleavedComponents			= 0;
 	retVal.numXFBSeparateAttribs				= 0;
 	retVal.numXFBSeparateComponents				= 0;
+	retVal.fragmentOutputMaxBinding				= -1; // see above
 
 	for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx)
 	{
@@ -1372,8 +1402,12 @@
 
 		retVal.numCombinedOutputResources		+= getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeImage);
 		retVal.numCombinedOutputResources		+= getNumShaderBlocks(shader, glu::STORAGE_BUFFER);
+
 		if (shader->getType() == glu::SHADERTYPE_FRAGMENT)
+		{
 			retVal.numCombinedOutputResources += getNumVectors(shader, glu::STORAGE_OUT);
+			retVal.fragmentOutputMaxBinding = de::max(retVal.fragmentOutputMaxBinding, getFragmentOutputMaxLocation(shader));
+		}
 	}
 
 	if (program->getTransformFeedbackMode() == GL_INTERLEAVED_ATTRIBS)
diff --git a/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.hpp b/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.hpp
index 0117013..ac24fce 100644
--- a/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.hpp
+++ b/modules/gles31/functional/es31fProgramInterfaceDefinitionUtil.hpp
@@ -147,6 +147,7 @@
 	int numXFBInterleavedComponents;
 	int numXFBSeparateAttribs;
 	int numXFBSeparateComponents;
+	int fragmentOutputMaxBinding;
 };
 
 } // ProgramInterfaceDefinition
diff --git a/modules/gles31/functional/es31fProgramInterfaceQueryTestCase.cpp b/modules/gles31/functional/es31fProgramInterfaceQueryTestCase.cpp
index 728d13a..a8a86e9 100644
--- a/modules/gles31/functional/es31fProgramInterfaceQueryTestCase.cpp
+++ b/modules/gles31/functional/es31fProgramInterfaceQueryTestCase.cpp
@@ -2117,7 +2117,7 @@
 		{ GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE,					usage.atomicCounterBufferMaxSize			},
 		{ GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS,				usage.numAtomicCounterBuffers				},
 		{ GL_MAX_COMBINED_ATOMIC_COUNTERS,						usage.numAtomicCounters						},
-		{ GL_MAX_IMAGE_UNITS,									usage.maxImageBinding						},
+		{ GL_MAX_IMAGE_UNITS,									usage.maxImageBinding+1						},
 		{ GL_MAX_COMBINED_IMAGE_UNIFORMS,						usage.numCombinedImages						},
 		{ GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS,				usage.shaderStorageBufferMaxBinding+1		},
 		{ GL_MAX_SHADER_STORAGE_BLOCK_SIZE,						usage.shaderStorageBufferMaxSize			},
@@ -2125,6 +2125,7 @@
 		{ GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,		usage.numXFBInterleavedComponents			},
 		{ GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,			usage.numXFBSeparateAttribs					},
 		{ GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,		usage.numXFBSeparateComponents				},
+		{ GL_MAX_DRAW_BUFFERS,									usage.fragmentOutputMaxBinding+1			},
 	};
 
 	bool allOk = true;
diff --git a/modules/gles31/functional/es31fProgramInterfaceQueryTests.cpp b/modules/gles31/functional/es31fProgramInterfaceQueryTests.cpp
index cf22cb1..1df5f89 100644
--- a/modules/gles31/functional/es31fProgramInterfaceQueryTests.cpp
+++ b/modules/gles31/functional/es31fProgramInterfaceQueryTests.cpp
@@ -4839,7 +4839,7 @@
 		}
 		// .var_array_explicit_location
 		{
-			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(2)));
+			const ResourceDefinition::Node::SharedPtr layout	(new ResourceDefinition::LayoutQualifier(output, glu::Layout(1)));
 			const ResourceDefinition::Node::SharedPtr arrayElem	(new ResourceDefinition::ArrayElement(layout));
 			const ResourceDefinition::Node::SharedPtr variable	(new ResourceDefinition::Variable(arrayElem, glu::TYPE_FLOAT_VEC4));
 			targetGroup->addChild(new ResourceTestCase(context, variable, ProgramResourceQueryTestTarget(PROGRAMINTERFACE_PROGRAM_OUTPUT, PROGRAMRESOURCEPROP_LOCATION), "var_array_explicit_location"));