es31fProgramInterfaceDefinition.cpp revision c423ce6164cdd88c8c3e47bec4ec34476743042a
1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Program interface
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fProgramInterfaceDefinition.hpp"
25#include "gluVarType.hpp"
26#include "gluShaderProgram.hpp"
27#include "deSTLUtil.hpp"
28#include "glwEnums.hpp"
29
30#include <set>
31
32namespace deqp
33{
34namespace gles31
35{
36namespace Functional
37{
38namespace ProgramInterfaceDefinition
39{
40namespace
41{
42
43static const glu::ShaderType s_shaderStageOrder[] =
44{
45	glu::SHADERTYPE_COMPUTE,
46
47	glu::SHADERTYPE_VERTEX,
48	glu::SHADERTYPE_TESSELLATION_CONTROL,
49	glu::SHADERTYPE_TESSELLATION_EVALUATION,
50	glu::SHADERTYPE_GEOMETRY,
51	glu::SHADERTYPE_FRAGMENT
52};
53
54// s_shaderStageOrder does not contain ShaderType_LAST
55DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_shaderStageOrder) == glu::SHADERTYPE_LAST);
56
57static bool containsMatchingSubtype (const glu::VarType& varType, bool (*predicate)(glu::DataType))
58{
59	if (varType.isBasicType() && predicate(varType.getBasicType()))
60		return true;
61
62	if (varType.isArrayType())
63		return containsMatchingSubtype(varType.getElementType(), predicate);
64
65	if (varType.isStructType())
66		for (int memberNdx = 0; memberNdx < varType.getStructPtr()->getNumMembers(); ++memberNdx)
67			if (containsMatchingSubtype(varType.getStructPtr()->getMember(memberNdx).getType(), predicate))
68				return true;
69
70	return false;
71}
72
73static bool containsMatchingSubtype (const std::vector<glu::VariableDeclaration>& decls, bool (*predicate)(glu::DataType))
74{
75	for (int varNdx = 0; varNdx < (int)decls.size(); ++varNdx)
76		if (containsMatchingSubtype(decls[varNdx].varType, predicate))
77			return true;
78	return false;
79}
80
81static bool isOpaqueType (glu::DataType type)
82{
83	return	glu::isDataTypeAtomicCounter(type)	||
84			glu::isDataTypeImage(type)			||
85			glu::isDataTypeSampler(type);
86}
87
88static int getShaderStageIndex (glu::ShaderType stage)
89{
90	const glu::ShaderType* const it = std::find(DE_ARRAY_BEGIN(s_shaderStageOrder), DE_ARRAY_END(s_shaderStageOrder), stage);
91
92	if (it == DE_ARRAY_END(s_shaderStageOrder))
93		return -1;
94	else
95	{
96		const int index = (int)(it - DE_ARRAY_BEGIN(s_shaderStageOrder));
97		return index;
98	}
99}
100
101} // anonymous
102
103Shader::Shader (glu::ShaderType type, glu::GLSLVersion version)
104	: m_shaderType	(type)
105	, m_version		(version)
106{
107}
108
109Shader::~Shader (void)
110{
111}
112
113static bool isIllegalVertexInput (const glu::VarType& varType)
114{
115	// booleans, opaque types, arrays, structs are not allowed as inputs
116	if (!varType.isBasicType())
117		return true;
118	if (glu::isDataTypeBoolOrBVec(varType.getBasicType()))
119		return true;
120	return false;
121}
122
123static bool isIllegalVertexOutput (const glu::VarType& varType, bool insideAStruct = false, bool insideAnArray = false)
124{
125	// booleans, opaque types, arrays of arrays, arrays of structs, array in struct, struct struct are not allowed as vertex outputs
126
127	if (varType.isBasicType())
128	{
129		const bool isOpaqueType = !glu::isDataTypeScalar(varType.getBasicType()) && !glu::isDataTypeVector(varType.getBasicType()) && !glu::isDataTypeMatrix(varType.getBasicType());
130
131		if (glu::isDataTypeBoolOrBVec(varType.getBasicType()))
132			return true;
133
134		if (isOpaqueType)
135			return true;
136
137		return false;
138	}
139	else if (varType.isArrayType())
140	{
141		if (insideAnArray || insideAStruct)
142			return true;
143
144		return isIllegalVertexOutput(varType.getElementType(), insideAStruct, true);
145	}
146	else if (varType.isStructType())
147	{
148		if (insideAnArray || insideAStruct)
149			return true;
150
151		for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx)
152			if (isIllegalVertexOutput(varType.getStructPtr()->getMember(ndx).getType(), true, insideAnArray))
153				return true;
154
155		return false;
156	}
157	else
158	{
159		DE_ASSERT(false);
160		return true;
161	}
162}
163
164static bool isIllegalFragmentInput (const glu::VarType& varType)
165{
166	return isIllegalVertexOutput(varType);
167}
168
169static bool isIllegalFragmentOutput (const glu::VarType& varType, bool insideAnArray = false)
170{
171	// booleans, opaque types, matrices, structs, arrays of arrays are not allowed as outputs
172
173	if (varType.isBasicType())
174	{
175		const bool isOpaqueType = !glu::isDataTypeScalar(varType.getBasicType()) && !glu::isDataTypeVector(varType.getBasicType()) && !glu::isDataTypeMatrix(varType.getBasicType());
176
177		if (glu::isDataTypeBoolOrBVec(varType.getBasicType()) || isOpaqueType || glu::isDataTypeMatrix(varType.getBasicType()))
178			return true;
179		return false;
180	}
181	else if (varType.isArrayType())
182	{
183		if (insideAnArray)
184			return true;
185		return isIllegalFragmentOutput(varType.getElementType(), true);
186	}
187	else if (varType.isStructType())
188		return true;
189	else
190	{
191		DE_ASSERT(false);
192		return true;
193	}
194}
195
196static bool isTypeIntegerOrContainsIntegers (const glu::VarType& varType)
197{
198	if (varType.isBasicType())
199		return glu::isDataTypeIntOrIVec(varType.getBasicType()) || glu::isDataTypeUintOrUVec(varType.getBasicType());
200	else if (varType.isArrayType())
201		return isTypeIntegerOrContainsIntegers(varType.getElementType());
202	else if (varType.isStructType())
203	{
204		for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx)
205			if (isTypeIntegerOrContainsIntegers(varType.getStructPtr()->getMember(ndx).getType()))
206				return true;
207		return false;
208	}
209	else
210	{
211		DE_ASSERT(false);
212		return true;
213	}
214}
215
216bool Shader::isValid (void) const
217{
218	// Default block variables
219	{
220		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
221		{
222			// atomic declaration in the default block without binding
223			if (m_defaultBlock.variables[varNdx].layout.binding == -1 &&
224				containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeAtomicCounter))
225				return false;
226
227			// atomic declaration in a struct
228			if (m_defaultBlock.variables[varNdx].varType.isStructType() &&
229				containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeAtomicCounter))
230				return false;
231
232			// Unsupported layout qualifiers
233
234			if (m_defaultBlock.variables[varNdx].layout.matrixOrder != glu::MATRIXORDER_LAST)
235				return false;
236
237			if (containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeSampler))
238			{
239				const glu::Layout layoutWithLocationAndBinding(m_defaultBlock.variables[varNdx].layout.location, m_defaultBlock.variables[varNdx].layout.binding);
240
241				if (m_defaultBlock.variables[varNdx].layout != layoutWithLocationAndBinding)
242					return false;
243			}
244		}
245	}
246
247	// Interface blocks
248	{
249		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
250		{
251			// ES31 disallows interface block array arrays
252			if (m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.size() > 1)
253				return false;
254
255			// Interface block arrays must have instance name
256			if (!m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty() && m_defaultBlock.interfaceBlocks[interfaceNdx].instanceName.empty())
257				return false;
258
259			// Opaque types in interface block
260			if (containsMatchingSubtype(m_defaultBlock.interfaceBlocks[interfaceNdx].variables, isOpaqueType))
261				return false;
262		}
263	}
264
265	// Shader type specific
266
267	if (m_shaderType == glu::SHADERTYPE_VERTEX)
268	{
269		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
270		{
271			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && isIllegalVertexInput(m_defaultBlock.variables[varNdx].varType))
272				return false;
273			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && isIllegalVertexOutput(m_defaultBlock.variables[varNdx].varType))
274				return false;
275			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && m_defaultBlock.variables[varNdx].interpolation != glu::INTERPOLATION_FLAT && isTypeIntegerOrContainsIntegers(m_defaultBlock.variables[varNdx].varType))
276				return false;
277		}
278		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
279		{
280			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN			||
281				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN	||
282				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT)
283			{
284				return false;
285			}
286		}
287	}
288	else if (m_shaderType == glu::SHADERTYPE_FRAGMENT)
289	{
290		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
291		{
292			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && isIllegalFragmentInput(m_defaultBlock.variables[varNdx].varType))
293				return false;
294			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && m_defaultBlock.variables[varNdx].interpolation != glu::INTERPOLATION_FLAT && isTypeIntegerOrContainsIntegers(m_defaultBlock.variables[varNdx].varType))
295				return false;
296			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && isIllegalFragmentOutput(m_defaultBlock.variables[varNdx].varType))
297				return false;
298		}
299		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
300		{
301			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN	||
302				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT		||
303				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT)
304			{
305				return false;
306			}
307		}
308	}
309	else if (m_shaderType == glu::SHADERTYPE_COMPUTE)
310	{
311		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
312		{
313			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN			||
314				m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN	||
315				m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT		||
316				m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT)
317			{
318				return false;
319			}
320		}
321		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
322		{
323			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN			||
324				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN	||
325				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT		||
326				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT)
327			{
328				return false;
329			}
330		}
331	}
332	else if (m_shaderType == glu::SHADERTYPE_GEOMETRY)
333	{
334		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
335		{
336			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN	||
337				m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT)
338			{
339				return false;
340			}
341			// arrayed input
342			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType())
343				return false;
344		}
345		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
346		{
347			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN	||
348				m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT)
349			{
350				return false;
351			}
352			// arrayed input
353			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty())
354				return false;
355		}
356	}
357	else if (m_shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL)
358	{
359		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
360		{
361			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN)
362				return false;
363			// arrayed input
364			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType())
365				return false;
366			// arrayed output
367			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && !m_defaultBlock.variables[varNdx].varType.isArrayType())
368				return false;
369		}
370		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
371		{
372			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN)
373				return false;
374			// arrayed input
375			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty())
376				return false;
377			// arrayed output
378			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty())
379				return false;
380		}
381	}
382	else if (m_shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION)
383	{
384		for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx)
385		{
386			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT)
387				return false;
388			// arrayed input
389			if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType())
390				return false;
391		}
392		for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
393		{
394			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT)
395				return false;
396			// arrayed input
397			if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty())
398				return false;
399		}
400	}
401	else
402		DE_ASSERT(false);
403
404	return true;
405}
406
407Program::Program (void)
408	: m_separable				(false)
409	, m_xfbMode					(0)
410	, m_geoNumOutputVertices	(0)
411	, m_tessNumOutputVertices	(0)
412{
413}
414
415static void collectStructPtrs (std::set<const glu::StructType*>& dst, const glu::VarType& type)
416{
417	if (type.isArrayType())
418		collectStructPtrs(dst, type.getElementType());
419	else if (type.isStructType())
420	{
421		dst.insert(type.getStructPtr());
422
423		for (int memberNdx = 0; memberNdx < type.getStructPtr()->getNumMembers(); ++memberNdx)
424			collectStructPtrs(dst, type.getStructPtr()->getMember(memberNdx).getType());
425	}
426}
427
428Program::~Program (void)
429{
430	// delete shader struct types, need to be done by the program since shaders might share struct types
431	{
432		std::set<const glu::StructType*> structTypes;
433
434		for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx)
435		{
436			for (int varNdx = 0; varNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.variables.size(); ++varNdx)
437				collectStructPtrs(structTypes, m_shaders[shaderNdx]->m_defaultBlock.variables[varNdx].varType);
438
439			for (int interfaceNdx = 0; interfaceNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx)
440				for (int varNdx = 0; varNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks[interfaceNdx].variables.size(); ++varNdx)
441					collectStructPtrs(structTypes, m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks[interfaceNdx].variables[varNdx].varType);
442		}
443
444		for (std::set<const glu::StructType*>::iterator it = structTypes.begin(); it != structTypes.end(); ++it)
445			delete *it;
446	}
447
448	for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx)
449		delete m_shaders[shaderNdx];
450	m_shaders.clear();
451}
452
453Shader* Program::addShader (glu::ShaderType type, glu::GLSLVersion version)
454{
455	DE_ASSERT(type < glu::SHADERTYPE_LAST);
456
457	Shader* shader;
458
459	// make sure push_back() cannot throw
460	m_shaders.reserve(m_shaders.size() + 1);
461
462	shader = new Shader(type, version);
463	m_shaders.push_back(shader);
464
465	return shader;
466}
467
468void Program::setSeparable (bool separable)
469{
470	m_separable = separable;
471}
472
473bool Program::isSeparable (void) const
474{
475	return m_separable;
476}
477
478const std::vector<Shader*>& Program::getShaders (void) const
479{
480	return m_shaders;
481}
482
483glu::ShaderType Program::getFirstStage (void) const
484{
485	const int	nullValue	= DE_LENGTH_OF_ARRAY(s_shaderStageOrder);
486	int			firstStage	= nullValue;
487
488	for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx)
489	{
490		const int index = getShaderStageIndex(m_shaders[shaderNdx]->getType());
491		if (index != -1)
492			firstStage = de::min(firstStage, index);
493	}
494
495	if (firstStage == nullValue)
496		return glu::SHADERTYPE_LAST;
497	else
498		return s_shaderStageOrder[firstStage];
499}
500
501glu::ShaderType Program::getLastStage (void) const
502{
503	const int	nullValue	= -1;
504	int			lastStage	= nullValue;
505
506	for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx)
507	{
508		const int index = getShaderStageIndex(m_shaders[shaderNdx]->getType());
509		if (index != -1)
510			lastStage = de::max(lastStage, index);
511	}
512
513	if (lastStage == nullValue)
514		return glu::SHADERTYPE_LAST;
515	else
516		return s_shaderStageOrder[lastStage];
517}
518
519bool Program::hasStage (glu::ShaderType stage) const
520{
521	for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx)
522	{
523		if (m_shaders[shaderNdx]->getType() == stage)
524			return true;
525	}
526	return false;
527}
528
529void Program::addTransformFeedbackVarying (const std::string& varName)
530{
531	m_xfbVaryings.push_back(varName);
532}
533
534const std::vector<std::string>& Program::getTransformFeedbackVaryings (void) const
535{
536	return m_xfbVaryings;
537}
538
539void Program::setTransformFeedbackMode (deUint32 mode)
540{
541	m_xfbMode = mode;
542}
543
544deUint32 Program::getTransformFeedbackMode (void) const
545{
546	return m_xfbMode;
547}
548
549deUint32 Program::getGeometryNumOutputVertices (void) const
550{
551	return m_geoNumOutputVertices;
552}
553
554void Program::setGeometryNumOutputVertices (deUint32 vertices)
555{
556	m_geoNumOutputVertices = vertices;
557}
558
559deUint32 Program::getTessellationNumOutputPatchVertices (void) const
560{
561	return m_tessNumOutputVertices;
562}
563
564void Program::setTessellationNumOutputPatchVertices (deUint32 vertices)
565{
566	m_tessNumOutputVertices = vertices;
567}
568
569bool Program::isValid (void) const
570{
571	bool computePresent			= false;
572	bool vertexPresent			= false;
573	bool fragmentPresent		= false;
574	bool tessControlPresent		= false;
575	bool tessEvalPresent		= false;
576	bool geometryPresent		= false;
577
578	if (m_shaders.empty())
579		return false;
580
581	for (int ndx = 0; ndx < (int)m_shaders.size(); ++ndx)
582		if (!m_shaders[ndx]->isValid())
583			return false;
584
585	// same version
586	for (int ndx = 1; ndx < (int)m_shaders.size(); ++ndx)
587		if (m_shaders[0]->getVersion() != m_shaders[ndx]->getVersion())
588			return false;
589
590	for (int ndx = 0; ndx < (int)m_shaders.size(); ++ndx)
591	{
592		switch (m_shaders[ndx]->getType())
593		{
594			case glu::SHADERTYPE_COMPUTE:					computePresent = true;		break;
595			case glu::SHADERTYPE_VERTEX:					vertexPresent = true;		break;
596			case glu::SHADERTYPE_FRAGMENT:					fragmentPresent = true;		break;
597			case glu::SHADERTYPE_TESSELLATION_CONTROL:		tessControlPresent = true;	break;
598			case glu::SHADERTYPE_TESSELLATION_EVALUATION:	tessEvalPresent = true;		break;
599			case glu::SHADERTYPE_GEOMETRY:					geometryPresent = true;		break;
600			default:
601				DE_ASSERT(false);
602				break;
603		}
604	}
605	// compute present -> no other stages present
606	{
607		const bool nonComputePresent = vertexPresent || fragmentPresent || tessControlPresent || tessEvalPresent || geometryPresent;
608		if (computePresent && nonComputePresent)
609			return false;
610	}
611
612	// must contain both vertex and fragment shaders
613	if (!computePresent && !m_separable)
614	{
615		if (!vertexPresent || !fragmentPresent)
616			return false;
617	}
618
619	// tess.Eval present <=> tess.Control present
620	if (!m_separable)
621	{
622		if (tessEvalPresent != tessControlPresent)
623			return false;
624	}
625
626	if ((m_tessNumOutputVertices != 0) != (tessControlPresent || tessEvalPresent))
627		return false;
628
629	if ((m_geoNumOutputVertices != 0) != geometryPresent)
630		return false;
631
632	return true;
633}
634
635} // ProgramInterfaceDefinition
636} // Functional
637} // gles31
638} // deqp
639