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 Compute Shader Built-in variable tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fComputeShaderBuiltinVarTests.hpp"
25 #include "gluShaderProgram.hpp"
26 #include "gluShaderUtil.hpp"
27 #include "gluRenderContext.hpp"
28 #include "gluObjectWrapper.hpp"
29 #include "gluProgramInterfaceQuery.hpp"
30 #include "tcuVector.hpp"
31 #include "tcuTestLog.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "deSharedPtr.hpp"
34 #include "deStringUtil.hpp"
35 #include "glwFunctions.hpp"
36 #include "glwEnums.hpp"
37 
38 #include <map>
39 
40 namespace deqp
41 {
42 namespace gles31
43 {
44 namespace Functional
45 {
46 
47 using std::string;
48 using std::vector;
49 using std::map;
50 using tcu::TestLog;
51 using tcu::UVec3;
52 using tcu::IVec3;
53 
54 using namespace glu;
55 
56 template<typename T, int Size>
57 struct LexicalCompareVec
58 {
operator ()deqp::gles31::Functional::LexicalCompareVec59 	inline bool operator() (const tcu::Vector<T, Size>& a, const tcu::Vector<T, Size>& b) const
60 	{
61 		for (int ndx = 0; ndx < Size; ndx++)
62 		{
63 			if (a[ndx] < b[ndx])
64 				return true;
65 			else if (a[ndx] > b[ndx])
66 				return false;
67 		}
68 		return false;
69 	}
70 };
71 
72 typedef de::SharedPtr<glu::ShaderProgram>										ShaderProgramSp;
73 typedef std::map<tcu::UVec3, ShaderProgramSp, LexicalCompareVec<deUint32, 3> >	LocalSizeProgramMap;
74 
75 class ComputeBuiltinVarCase : public TestCase
76 {
77 public:
78 							ComputeBuiltinVarCase	(Context& context, const char* name, const char* varName, DataType varType);
79 							~ComputeBuiltinVarCase	(void);
80 
81 	void					init					(void);
82 	void					deinit					(void);
83 	IterateResult			iterate					(void);
84 
85 	virtual UVec3			computeReference		(const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const = 0;
86 
87 protected:
88 	struct SubCase
89 	{
90 		UVec3		localSize;
91 		UVec3		numWorkGroups;
92 
SubCasedeqp::gles31::Functional::ComputeBuiltinVarCase::SubCase93 		SubCase (void) {}
SubCasedeqp::gles31::Functional::ComputeBuiltinVarCase::SubCase94 		SubCase (const UVec3& localSize_, const UVec3& numWorkGroups_) : localSize(localSize_), numWorkGroups(numWorkGroups_) {}
95 	};
96 
97 	vector<SubCase>			m_subCases;
98 
99 private:
100 							ComputeBuiltinVarCase	(const ComputeBuiltinVarCase& other);
101 	ComputeBuiltinVarCase&	operator=				(const ComputeBuiltinVarCase& other);
102 
103 	deUint32				getProgram				(const UVec3& localSize);
104 
105 	const string			m_varName;
106 	const DataType			m_varType;
107 
108 	LocalSizeProgramMap		m_progMap;
109 	int						m_subCaseNdx;
110 };
111 
ComputeBuiltinVarCase(Context & context,const char * name,const char * varName,DataType varType)112 ComputeBuiltinVarCase::ComputeBuiltinVarCase (Context& context, const char* name, const char* varName, DataType varType)
113 	: TestCase		(context, name, varName)
114 	, m_varName		(varName)
115 	, m_varType		(varType)
116 	, m_subCaseNdx	(0)
117 {
118 }
119 
~ComputeBuiltinVarCase(void)120 ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void)
121 {
122 	ComputeBuiltinVarCase::deinit();
123 }
124 
init(void)125 void ComputeBuiltinVarCase::init (void)
126 {
127 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
128 	m_subCaseNdx = 0;
129 }
130 
deinit(void)131 void ComputeBuiltinVarCase::deinit (void)
132 {
133 	m_progMap.clear();
134 }
135 
genBuiltinVarSource(const string & varName,DataType varType,const UVec3 & localSize)136 static string genBuiltinVarSource (const string& varName, DataType varType, const UVec3& localSize)
137 {
138 	std::ostringstream src;
139 
140 	src << "#version 310 es\n"
141 		<< "layout (local_size_x = " << localSize.x() << ", local_size_y = " << localSize.y() << ", local_size_z = " << localSize.z() << ") in;\n"
142 		<< "uniform highp uvec2 u_stride;\n"
143 		<< "layout(binding = 0) buffer Output\n"
144 		<< "{\n"
145 		<< "	" << glu::getDataTypeName(varType) << " result[];\n"
146 		<< "} sb_out;\n"
147 		<< "\n"
148 		<< "void main (void)\n"
149 		<< "{\n"
150 		<< "	highp uint offset = u_stride.x*gl_GlobalInvocationID.z + u_stride.y*gl_GlobalInvocationID.y + gl_GlobalInvocationID.x;\n"
151 		<< "	sb_out.result[offset] = " << varName << ";\n"
152 		<< "}\n";
153 
154 	return src.str();
155 }
156 
getProgram(const UVec3 & localSize)157 deUint32 ComputeBuiltinVarCase::getProgram (const UVec3& localSize)
158 {
159 	LocalSizeProgramMap::const_iterator cachePos = m_progMap.find(localSize);
160 	if (cachePos != m_progMap.end())
161 		return cachePos->second->getProgram();
162 	else
163 	{
164 		ShaderProgramSp program(new ShaderProgram(m_context.getRenderContext(),
165 												  ProgramSources() << ComputeSource(genBuiltinVarSource(m_varName, m_varType, localSize))));
166 
167 		// Log all compiled programs.
168 		m_testCtx.getLog() << *program;
169 		if (!program->isOk())
170 			throw tcu::TestError("Compile failed");
171 
172 		m_progMap[localSize] = program;
173 		return program->getProgram();
174 	}
175 }
176 
readResultVec(const deUint32 * ptr,int numComps)177 static inline UVec3 readResultVec (const deUint32* ptr, int numComps)
178 {
179 	UVec3 res;
180 	for (int ndx = 0; ndx < numComps; ndx++)
181 		res[ndx] = ptr[ndx];
182 	return res;
183 }
184 
compareComps(const UVec3 & a,const UVec3 & b,int numComps)185 static inline bool compareComps (const UVec3& a, const UVec3& b, int numComps)
186 {
187 	DE_ASSERT(numComps == 1 || numComps == 3);
188 	return numComps == 3 ? tcu::allEqual(a, b) : a.x() == b.x();
189 }
190 
191 struct LogComps
192 {
193 	const UVec3&	v;
194 	int				numComps;
195 
LogCompsdeqp::gles31::Functional::LogComps196 	LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {}
197 };
198 
operator <<(std::ostream & str,const LogComps & c)199 static inline std::ostream& operator<< (std::ostream& str, const LogComps& c)
200 {
201 	DE_ASSERT(c.numComps == 1 || c.numComps == 3);
202 	return c.numComps == 3 ? str << c.v : str << c.v.x();
203 }
204 
iterate(void)205 ComputeBuiltinVarCase::IterateResult ComputeBuiltinVarCase::iterate (void)
206 {
207 	const tcu::ScopedLogSection		section			(m_testCtx.getLog(), string("Iteration") + de::toString(m_subCaseNdx), string("Iteration ") + de::toString(m_subCaseNdx));
208 	const glw::Functions&			gl				= m_context.getRenderContext().getFunctions();
209 	const SubCase&					subCase			= m_subCases[m_subCaseNdx];
210 	const deUint32					program			= getProgram(subCase.localSize);
211 
212 	const tcu::UVec3				globalSize		= subCase.localSize*subCase.numWorkGroups;
213 	const tcu::UVec2				stride			(globalSize[0]*globalSize[1], globalSize[0]);
214 	const deUint32					numInvocations	= subCase.localSize[0]*subCase.localSize[1]*subCase.localSize[2]*subCase.numWorkGroups[0]*subCase.numWorkGroups[1]*subCase.numWorkGroups[2];
215 
216 	const deUint32					outVarIndex		= gl.getProgramResourceIndex(program, GL_BUFFER_VARIABLE, "Output.result");
217 	const InterfaceVariableInfo		outVarInfo		= getProgramInterfaceVariableInfo(gl, program, GL_BUFFER_VARIABLE, outVarIndex);
218 	const deUint32					bufferSize		= numInvocations*outVarInfo.arrayStride;
219 	Buffer							outputBuffer	(m_context.getRenderContext());
220 
221 	TCU_CHECK(outVarInfo.arraySize == 0); // Unsized variable.
222 
223 	m_testCtx.getLog() << TestLog::Message << "Number of work groups = " << subCase.numWorkGroups << TestLog::EndMessage
224 					   << TestLog::Message << "Work group size = " << subCase.localSize << TestLog::EndMessage;
225 
226 	gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
227 	gl.bufferData(GL_SHADER_STORAGE_BUFFER, (glw::GLsizeiptr)bufferSize, DE_NULL, GL_STREAM_READ);
228 	gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
229 	GLU_EXPECT_NO_ERROR(gl.getError(), "Buffer setup failed");
230 
231 	gl.useProgram(program);
232 	gl.uniform2uiv(gl.getUniformLocation(program, "u_stride"), 1, stride.getPtr());
233 	GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed");
234 
235 	gl.dispatchCompute(subCase.numWorkGroups[0], subCase.numWorkGroups[1], subCase.numWorkGroups[2]);
236 	GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
237 
238 	{
239 		const void*	ptr				= gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, bufferSize, GL_MAP_READ_BIT);
240 		int			numFailed		= 0;
241 		const int	numScalars		= getDataTypeScalarSize(m_varType);
242 		const int	maxLogPrints	= 10;
243 
244 		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed");
245 		TCU_CHECK(ptr);
246 
247 		for (deUint32 groupZ = 0; groupZ < subCase.numWorkGroups.z(); groupZ++)
248 		for (deUint32 groupY = 0; groupY < subCase.numWorkGroups.y(); groupY++)
249 		for (deUint32 groupX = 0; groupX < subCase.numWorkGroups.x(); groupX++)
250 		for (deUint32 localZ = 0; localZ < subCase.localSize.z(); localZ++)
251 		for (deUint32 localY = 0; localY < subCase.localSize.y(); localY++)
252 		for (deUint32 localX = 0; localX < subCase.localSize.x(); localX++)
253 		{
254 			const UVec3			refGroupID		(groupX, groupY, groupZ);
255 			const UVec3			refLocalID		(localX, localY, localZ);
256 			const UVec3			refGlobalID		= refGroupID * subCase.localSize + refLocalID;
257 			const deUint32		refOffset		= stride.x()*refGlobalID.z() + stride.y()*refGlobalID.y() + refGlobalID.x();
258 			const UVec3			refValue		= computeReference(subCase.numWorkGroups, subCase.localSize, refGroupID, refLocalID);
259 
260 			const deUint32*		resPtr			= (const deUint32*)((const deUint8*)ptr + refOffset*outVarInfo.arrayStride);
261 			const UVec3			resValue		= readResultVec(resPtr, numScalars);
262 
263 			if (!compareComps(refValue, resValue, numScalars))
264 			{
265 				if (numFailed < maxLogPrints)
266 					m_testCtx.getLog() << TestLog::Message << "ERROR: comparison failed at offset " << refOffset
267 														   << ": expected " << LogComps(refValue, numScalars)
268 														   << ", got " << LogComps(resValue, numScalars)
269 									   << TestLog::EndMessage;
270 				else if (numFailed == maxLogPrints)
271 					m_testCtx.getLog() << TestLog::Message << "..." << TestLog::EndMessage;
272 
273 				numFailed += 1;
274 			}
275 		}
276 
277 		m_testCtx.getLog() << TestLog::Message << (numInvocations-numFailed) << " / " << numInvocations << " values passed" << TestLog::EndMessage;
278 
279 		if (numFailed > 0)
280 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Comparison failed");
281 
282 		gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
283 	}
284 
285 	m_subCaseNdx += 1;
286 	return (m_subCaseNdx < (int)m_subCases.size() && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP;
287 }
288 
289 // Test cases
290 
291 class NumWorkGroupsCase : public ComputeBuiltinVarCase
292 {
293 public:
NumWorkGroupsCase(Context & context)294 	NumWorkGroupsCase (Context& context)
295 		: ComputeBuiltinVarCase(context, "num_work_groups", "gl_NumWorkGroups", TYPE_UINT_VEC3)
296 	{
297 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
298 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
299 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
300 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
301 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
302 		m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
303 	}
304 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const305 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
306 	{
307 		DE_UNREF(numWorkGroups);
308 		DE_UNREF(workGroupSize);
309 		DE_UNREF(workGroupID);
310 		DE_UNREF(localInvocationID);
311 		return numWorkGroups;
312 	}
313 };
314 
315 class WorkGroupSizeCase : public ComputeBuiltinVarCase
316 {
317 public:
WorkGroupSizeCase(Context & context)318 	WorkGroupSizeCase (Context& context)
319 		: ComputeBuiltinVarCase(context, "work_group_size", "gl_WorkGroupSize", TYPE_UINT_VEC3)
320 	{
321 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
322 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3)));
323 		m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1)));
324 		m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5)));
325 		m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1)));
326 		m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1)));
327 		m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1)));
328 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
329 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
330 	}
331 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const332 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
333 	{
334 		DE_UNREF(numWorkGroups);
335 		DE_UNREF(workGroupID);
336 		DE_UNREF(localInvocationID);
337 		return workGroupSize;
338 	}
339 };
340 
341 class WorkGroupIDCase : public ComputeBuiltinVarCase
342 {
343 public:
WorkGroupIDCase(Context & context)344 	WorkGroupIDCase (Context& context)
345 		: ComputeBuiltinVarCase(context, "work_group_id", "gl_WorkGroupID", TYPE_UINT_VEC3)
346 	{
347 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
348 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
349 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
350 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
351 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
352 		m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
353 	}
354 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const355 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
356 	{
357 		DE_UNREF(numWorkGroups);
358 		DE_UNREF(workGroupSize);
359 		DE_UNREF(localInvocationID);
360 		return workGroupID;
361 	}
362 };
363 
364 class LocalInvocationIDCase : public ComputeBuiltinVarCase
365 {
366 public:
LocalInvocationIDCase(Context & context)367 	LocalInvocationIDCase (Context& context)
368 		: ComputeBuiltinVarCase(context, "local_invocation_id", "gl_LocalInvocationID", TYPE_UINT_VEC3)
369 	{
370 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
371 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(2,7,3)));
372 		m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,1,1)));
373 		m_subCases.push_back(SubCase(UVec3(2,1,1), UVec3(1,3,5)));
374 		m_subCases.push_back(SubCase(UVec3(1,3,1), UVec3(1,1,1)));
375 		m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(1,1,1)));
376 		m_subCases.push_back(SubCase(UVec3(1,1,7), UVec3(3,3,1)));
377 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
378 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
379 	}
380 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const381 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
382 	{
383 		DE_UNREF(numWorkGroups);
384 		DE_UNREF(workGroupSize);
385 		DE_UNREF(workGroupID);
386 		return localInvocationID;
387 	}
388 };
389 
390 class GlobalInvocationIDCase : public ComputeBuiltinVarCase
391 {
392 public:
GlobalInvocationIDCase(Context & context)393 	GlobalInvocationIDCase (Context& context)
394 		: ComputeBuiltinVarCase(context, "global_invocation_id", "gl_GlobalInvocationID", TYPE_UINT_VEC3)
395 	{
396 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
397 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(52,1,1)));
398 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
399 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,78)));
400 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
401 		m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
402 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
403 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
404 	}
405 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const406 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
407 	{
408 		DE_UNREF(numWorkGroups);
409 		return workGroupID * workGroupSize + localInvocationID;
410 	}
411 };
412 
413 class LocalInvocationIndexCase : public ComputeBuiltinVarCase
414 {
415 public:
LocalInvocationIndexCase(Context & context)416 	LocalInvocationIndexCase (Context& context)
417 		: ComputeBuiltinVarCase(context, "local_invocation_index", "gl_LocalInvocationIndex", TYPE_UINT)
418 	{
419 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,1,1)));
420 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(1,39,1)));
421 		m_subCases.push_back(SubCase(UVec3(1,1,1), UVec3(4,7,11)));
422 		m_subCases.push_back(SubCase(UVec3(2,3,4), UVec3(4,7,11)));
423 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(1,1,1)));
424 		m_subCases.push_back(SubCase(UVec3(10,3,4), UVec3(3,1,2)));
425 	}
426 
computeReference(const UVec3 & numWorkGroups,const UVec3 & workGroupSize,const UVec3 & workGroupID,const UVec3 & localInvocationID) const427 	UVec3 computeReference (const UVec3& numWorkGroups, const UVec3& workGroupSize, const UVec3& workGroupID, const UVec3& localInvocationID) const
428 	{
429 		DE_UNREF(workGroupID);
430 		DE_UNREF(numWorkGroups);
431 		return UVec3(localInvocationID.z()*workGroupSize.x()*workGroupSize.y() + localInvocationID.y()*workGroupSize.x() + localInvocationID.x(), 0, 0);
432 	}
433 };
434 
ComputeShaderBuiltinVarTests(Context & context)435 ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context)
436 	: TestCaseGroup(context, "compute", "Compute Shader Builtin Variables")
437 {
438 }
439 
~ComputeShaderBuiltinVarTests(void)440 ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void)
441 {
442 }
443 
init(void)444 void ComputeShaderBuiltinVarTests::init (void)
445 {
446 	addChild(new NumWorkGroupsCase			(m_context));
447 	addChild(new WorkGroupSizeCase			(m_context));
448 	addChild(new WorkGroupIDCase			(m_context));
449 	addChild(new LocalInvocationIDCase		(m_context));
450 	addChild(new GlobalInvocationIDCase		(m_context));
451 	addChild(new LocalInvocationIndexCase	(m_context));
452 }
453 
454 } // Functional
455 } // gles31
456 } // deqp
457