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
40namespace deqp
41{
42namespace gles31
43{
44namespace Functional
45{
46
47using std::string;
48using std::vector;
49using std::map;
50using tcu::TestLog;
51using tcu::UVec3;
52using tcu::IVec3;
53
54using namespace glu;
55
56template<typename T, int Size>
57struct LexicalCompareVec
58{
59	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
72typedef de::SharedPtr<glu::ShaderProgram>										ShaderProgramSp;
73typedef std::map<tcu::UVec3, ShaderProgramSp, LexicalCompareVec<deUint32, 3> >	LocalSizeProgramMap;
74
75class ComputeBuiltinVarCase : public TestCase
76{
77public:
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
87protected:
88	struct SubCase
89	{
90		UVec3		localSize;
91		UVec3		numWorkGroups;
92
93		SubCase (void) {}
94		SubCase (const UVec3& localSize_, const UVec3& numWorkGroups_) : localSize(localSize_), numWorkGroups(numWorkGroups_) {}
95	};
96
97	vector<SubCase>			m_subCases;
98
99private:
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
112ComputeBuiltinVarCase::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
120ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void)
121{
122	ComputeBuiltinVarCase::deinit();
123}
124
125void ComputeBuiltinVarCase::init (void)
126{
127	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
128	m_subCaseNdx = 0;
129}
130
131void ComputeBuiltinVarCase::deinit (void)
132{
133	m_progMap.clear();
134}
135
136static 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
157deUint32 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
177static 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
185static 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
191struct LogComps
192{
193	const UVec3&	v;
194	int				numComps;
195
196	LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {}
197};
198
199static 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
205ComputeBuiltinVarCase::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
291class NumWorkGroupsCase : public ComputeBuiltinVarCase
292{
293public:
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
305	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
315class WorkGroupSizeCase : public ComputeBuiltinVarCase
316{
317public:
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
332	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
341class WorkGroupIDCase : public ComputeBuiltinVarCase
342{
343public:
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
355	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
364class LocalInvocationIDCase : public ComputeBuiltinVarCase
365{
366public:
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
381	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
390class GlobalInvocationIDCase : public ComputeBuiltinVarCase
391{
392public:
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
406	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
413class LocalInvocationIndexCase : public ComputeBuiltinVarCase
414{
415public:
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
427	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
435ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context)
436	: TestCaseGroup(context, "compute", "Compute Shader Builtin Variables")
437{
438}
439
440ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void)
441{
442}
443
444void 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