Home | History | Annotate | Download | only in functional
      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 {
     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 
     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 
     93 		SubCase (void) {}
     94 		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 
    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 
    120 ComputeBuiltinVarCase::~ComputeBuiltinVarCase (void)
    121 {
    122 	ComputeBuiltinVarCase::deinit();
    123 }
    124 
    125 void ComputeBuiltinVarCase::init (void)
    126 {
    127 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    128 	m_subCaseNdx = 0;
    129 }
    130 
    131 void ComputeBuiltinVarCase::deinit (void)
    132 {
    133 	m_progMap.clear();
    134 }
    135 
    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 
    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 
    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 
    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 
    196 	LogComps (const UVec3& v_, int numComps_) : v(v_), numComps(numComps_) {}
    197 };
    198 
    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 
    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:
    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 
    315 class WorkGroupSizeCase : public ComputeBuiltinVarCase
    316 {
    317 public:
    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 
    341 class WorkGroupIDCase : public ComputeBuiltinVarCase
    342 {
    343 public:
    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 
    364 class LocalInvocationIDCase : public ComputeBuiltinVarCase
    365 {
    366 public:
    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 
    390 class GlobalInvocationIDCase : public ComputeBuiltinVarCase
    391 {
    392 public:
    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 
    413 class LocalInvocationIndexCase : public ComputeBuiltinVarCase
    414 {
    415 public:
    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 
    435 ComputeShaderBuiltinVarTests::ComputeShaderBuiltinVarTests (Context& context)
    436 	: TestCaseGroup(context, "compute", "Compute Shader Builtin Variables")
    437 {
    438 }
    439 
    440 ComputeShaderBuiltinVarTests::~ComputeShaderBuiltinVarTests (void)
    441 {
    442 }
    443 
    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