1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 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 Long shader compilation stress tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3sLongShaderTests.hpp"
25 
26 #include "deRandom.hpp"
27 #include "deStringUtil.hpp"
28 #include "deString.h"
29 #include "tcuTestLog.hpp"
30 #include "gluRenderContext.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "glwFunctions.hpp"
33 #include "glwEnums.hpp"
34 
35 #include <string>
36 #include <set>
37 #include <map>
38 #include <cmath>
39 
40 using tcu::TestLog;
41 
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Stress
47 {
48 
49 namespace
50 {
51 
52 enum LongShaderCaseFlags
53 {
54 	CASE_REQUIRE_LINK_STATUS_OK	= 1
55 };
56 
getConstVertShaderSource(void)57 const char* getConstVertShaderSource (void)
58 {
59 	const char* const src =
60 		"#version 300 es\n"
61 		"void main ()\n"
62 		"{\n"
63 		"	gl_Position = vec4(0.0);\n"
64 		"}\n";
65 
66 	return src;
67 }
68 
getConstFragShaderSource(void)69 const char* getConstFragShaderSource (void)
70 {
71 	const char* const src =
72 		"#version 300 es\n"
73 		"layout(location = 0) out mediump vec4 o_fragColor;\n"
74 		"void main ()\n"
75 		"{\n"
76 		"	o_fragColor = vec4(0.0);\n"
77 		"}\n";
78 
79 	return src;
80 }
81 
getConstShaderSource(const glu::ShaderType shaderType)82 const char* getConstShaderSource (const glu::ShaderType shaderType)
83 {
84 	DE_ASSERT(shaderType == glu::SHADERTYPE_VERTEX || shaderType == glu::SHADERTYPE_FRAGMENT);
85 
86 	if (shaderType == glu::SHADERTYPE_VERTEX)
87 		return getConstVertShaderSource();
88 	else
89 		return getConstFragShaderSource();
90 }
91 
92 typedef std::set<std::string> ShaderScope;
93 
94 const char variableNamePrefixChars[] = "abcdefghijklmnopqrstuvwxyz";
95 
96 class NameGenerator
97 {
98 public:
NameGenerator(void)99 	NameGenerator (void)
100 		: m_scopeIndices		(1, 0)
101 		, m_currentScopeDepth	(1)
102 		, m_variableIndex		(0)
103 	{
104 	}
105 
beginScope(void)106 	void beginScope (void)
107 	{
108 		m_currentScopeDepth++;
109 
110 		if (m_scopeIndices.size() < (size_t)m_currentScopeDepth)
111 			m_scopeIndices.push_back(0);
112 		else
113 			m_scopeIndices[m_currentScopeDepth-1]++;
114 
115 		m_variableIndex = 0;
116 	}
117 
endScope(void)118 	void endScope (void)
119 	{
120 		DE_ASSERT(m_currentScopeDepth > 1);
121 
122 		m_currentScopeDepth--;
123 	}
124 
makePrefix(void)125 	std::string makePrefix (void)
126 	{
127 		std::string prefix;
128 
129 		for (int ndx = 0; ndx < m_currentScopeDepth; ndx++)
130 		{
131 			const int scopeIndex = m_scopeIndices[m_currentScopeDepth-1];
132 
133 			DE_ASSERT(scopeIndex < DE_LENGTH_OF_ARRAY(variableNamePrefixChars));
134 
135 			prefix += variableNamePrefixChars[scopeIndex];
136 		}
137 
138 		return prefix;
139 	}
140 
next(void)141 	std::string next (void)
142 	{
143 		m_variableIndex++;
144 
145 		return makePrefix() + de::toString(m_variableIndex);
146 	}
147 
makeNames(ShaderScope & scope,const deUint32 count)148 	void makeNames (ShaderScope& scope, const deUint32 count)
149 	{
150 		for (deUint32 ndx = 0; ndx < count; ndx++)
151 			scope.insert(next());
152 	}
153 
154 private:
155 	std::vector<int>	m_scopeIndices;
156 	int					m_currentScopeDepth;
157 	int					m_variableIndex;
158 };
159 
160 struct LongShaderSpec
161 {
162 	glu::ShaderType	shaderType;
163 	deUint32		opsTotal;
164 
165 	deUint32		variablesPerBlock;
166 	deUint32		opsPerExpression;
167 
LongShaderSpecdeqp::gles3::Stress::__anon1bbf273b0111::LongShaderSpec168 	LongShaderSpec (const glu::ShaderType shaderTypeInit, const deUint32 opsTotalInit)
169 		: shaderType		(shaderTypeInit)
170 		, opsTotal			(opsTotalInit)
171 		, variablesPerBlock	(deMaxu32(10, (deUint32)std::floor(std::sqrt((double)opsTotal))))
172 		, opsPerExpression	(deMinu32(10, variablesPerBlock / 2))
173 	{
174 	}
175 };
176 
177 // Generator for long test shaders
178 
179 class LongShaderGenerator
180 {
181 public:
182 								LongShaderGenerator		(de::Random& rnd, const LongShaderSpec& spec);
183 
184 	glu::ShaderSource			getSource				(void);
185 
186 private:
187 	de::Random					m_rnd;
188 	const LongShaderSpec		m_spec;
189 
190 	NameGenerator				m_nameGen;
191 
192 	std::vector<std::string>	m_varNames;
193 	std::vector<ShaderScope>	m_scopes;
194 
195 	std::string					m_source;
196 
197 	void						generateSource			(void);
198 
199 	std::string					getRandomVariableName	(void);
200 	std::string					getShaderOutputName		(void);
201 	std::string					makeExpression			(const std::vector<std::string>& varNames, const int numOps);
202 
203 	void						addIndent				(void);
204 	void						addLine					(const std::string& text);
205 
206 	void						beginBlock				(void);
207 	void						endBlock				(void);
208 };
209 
LongShaderGenerator(de::Random & rnd,const LongShaderSpec & spec)210 LongShaderGenerator::LongShaderGenerator (de::Random& rnd, const LongShaderSpec& spec)
211 	: m_rnd			(rnd)
212 	, m_spec		(spec)
213 {
214 	DE_ASSERT(m_spec.shaderType == glu::SHADERTYPE_VERTEX || m_spec.shaderType == glu::SHADERTYPE_FRAGMENT);
215 }
216 
getSource(void)217 glu::ShaderSource LongShaderGenerator::getSource (void)
218 {
219 	if (m_source.empty())
220 		generateSource();
221 
222 	return glu::ShaderSource(m_spec.shaderType, m_source);
223 }
224 
generateSource(void)225 void LongShaderGenerator::generateSource (void)
226 {
227 	deUint32 currentOpsTotal = 0;
228 
229 	m_source.clear();
230 
231 	addLine("#version 300 es");
232 
233 	if (m_spec.shaderType == glu::SHADERTYPE_FRAGMENT)
234 		addLine("layout(location = 0) out mediump vec4 o_fragColor;");
235 
236 	addLine("void main (void)");
237 	beginBlock();
238 
239 	while (currentOpsTotal < m_spec.opsTotal)
240 	{
241 		const bool					isLast	= (m_spec.opsTotal <= (currentOpsTotal + m_spec.opsPerExpression));
242 		const int					numOps	= isLast ? (m_spec.opsTotal - currentOpsTotal) : m_spec.opsPerExpression;
243 		const size_t				numVars	= numOps + 1;
244 
245 		const std::string			outName	= isLast ? getShaderOutputName() : getRandomVariableName();
246 		std::vector<std::string>	inNames	(numVars);
247 
248 		DE_ASSERT(numVars < m_varNames.size());
249 		m_rnd.choose(m_varNames.begin(), m_varNames.end(), inNames.begin(), (int)numVars);
250 
251 		{
252 			std::string expr = makeExpression(inNames, numOps);
253 
254 			if (isLast)
255 				addLine(outName + " = vec4(" + expr + ");");
256 			else
257 				addLine(outName + " = " + expr + ";");
258 		}
259 
260 		currentOpsTotal += numOps;
261 	}
262 
263 	while (!m_scopes.empty())
264 		endBlock();
265 }
266 
getRandomVariableName(void)267 std::string LongShaderGenerator::getRandomVariableName (void)
268 {
269 	return m_rnd.choose<std::string>(m_varNames.begin(), m_varNames.end());
270 }
271 
getShaderOutputName(void)272 std::string LongShaderGenerator::getShaderOutputName (void)
273 {
274 	return (m_spec.shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor";
275 }
276 
makeExpression(const std::vector<std::string> & varNames,const int numOps)277 std::string LongShaderGenerator::makeExpression (const std::vector<std::string>& varNames, const int numOps)
278 {
279 	const std::string	operators	= "+-*/";
280 	std::string			expr;
281 
282 	DE_ASSERT(varNames.size() > (size_t)numOps);
283 
284 	expr = varNames[0];
285 
286 	for (int ndx = 1; ndx <= numOps; ndx++)
287 	{
288 		const std::string	op		= std::string("") + m_rnd.choose<char>(operators.begin(), operators.end());
289 		const std::string	varName	= varNames[ndx];
290 
291 		expr += " " + op + " " + varName;
292 	}
293 
294 	return expr;
295 }
296 
297 
addIndent(void)298 void LongShaderGenerator::addIndent (void)
299 {
300 	m_source += std::string(m_scopes.size(), '\t');
301 }
302 
addLine(const std::string & text)303 void LongShaderGenerator::addLine (const std::string& text)
304 {
305 	addIndent();
306 	m_source += text + "\n";
307 }
308 
beginBlock(void)309 void LongShaderGenerator::beginBlock (void)
310 {
311 	ShaderScope scope;
312 
313 	addLine("{");
314 
315 	m_nameGen.beginScope();
316 	m_nameGen.makeNames(scope, m_spec.variablesPerBlock);
317 
318 	m_scopes.push_back(scope);
319 
320 	for (ShaderScope::const_iterator nameIter = scope.begin(); nameIter != scope.end(); nameIter++)
321 	{
322 		const std::string	varName		= *nameIter;
323 		const float			varValue	= m_rnd.getFloat();
324 
325 		addLine("mediump float " + varName + " = " + de::floatToString(varValue, 5) + "f;");
326 		m_varNames.push_back(varName);
327 	}
328 }
329 
endBlock(void)330 void LongShaderGenerator::endBlock (void)
331 {
332 	ShaderScope& scope = *(m_scopes.end()-1);
333 
334 	DE_ASSERT(!m_scopes.empty());
335 
336 	m_varNames.erase((m_varNames.begin() + (m_varNames.size() - scope.size())), m_varNames.end());
337 
338 	m_nameGen.endScope();
339 	m_scopes.pop_back();
340 
341 	addLine("}");
342 }
343 
344 } // anonymous
345 
346 // Stress test case for compilation of large shaders
347 
348 class LongShaderCompileStressCase : public TestCase
349 {
350 public:
351 							LongShaderCompileStressCase		(Context& context, const char* name, const char* desc, const LongShaderSpec& caseSpec, const deUint32 flags);
352 	virtual					~LongShaderCompileStressCase	(void);
353 
354 	void					init							(void);
355 
356 	IterateResult			iterate							(void);
357 
358 	void					verify							(const glu::ShaderProgram& program);
359 
360 private:
361 	const glu::ShaderType	m_shaderType;
362 	const deUint32			m_flags;
363 	de::Random				m_rnd;
364 	LongShaderGenerator		m_gen;
365 };
366 
LongShaderCompileStressCase(Context & context,const char * name,const char * desc,const LongShaderSpec & caseSpec,const deUint32 flags)367 LongShaderCompileStressCase::LongShaderCompileStressCase (Context& context, const char* name, const char* desc, const LongShaderSpec& caseSpec, const deUint32 flags)
368 	: TestCase		(context, name, desc)
369 	, m_shaderType	(caseSpec.shaderType)
370 	, m_flags		(flags)
371 	, m_rnd			(deStringHash(name) ^ 0xac9c91d)
372 	, m_gen			(m_rnd, caseSpec)
373 {
374 	DE_ASSERT(m_shaderType == glu::SHADERTYPE_VERTEX || m_shaderType == glu::SHADERTYPE_FRAGMENT);
375 }
376 
~LongShaderCompileStressCase(void)377 LongShaderCompileStressCase::~LongShaderCompileStressCase (void)
378 {
379 }
380 
init(void)381 void LongShaderCompileStressCase::init (void)
382 {
383 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
384 }
385 
iterate(void)386 tcu::TestCase::IterateResult LongShaderCompileStressCase::iterate (void)
387 {
388 	tcu::TestLog&				log			= m_testCtx.getLog();
389 	const glu::ShaderType		otherShader	= (m_shaderType == glu::SHADERTYPE_VERTEX) ? glu::SHADERTYPE_FRAGMENT : glu::SHADERTYPE_VERTEX;
390 	glu::ProgramSources			sources;
391 
392 	sources << m_gen.getSource();
393 	sources << glu::ShaderSource(otherShader, getConstShaderSource(otherShader));
394 
395 	{
396 		glu::ShaderProgram program(m_context.getRenderContext(), sources);
397 
398 		verify(program);
399 
400 		log << program;
401 	}
402 
403 	return STOP;
404 }
405 
verify(const glu::ShaderProgram & program)406 void LongShaderCompileStressCase::verify (const glu::ShaderProgram& program)
407 {
408 	tcu::TestLog&			log			= m_testCtx.getLog();
409 	const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
410 	const bool				isStrict	= (m_flags & CASE_REQUIRE_LINK_STATUS_OK) != 0;
411 	const glw::GLenum		errorCode	= gl.getError();
412 
413 	if (isStrict && !program.isOk())
414 	{
415 		log << TestLog::Message << "Fail, expected program to compile and link successfully." << TestLog::EndMessage;
416 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Linking failed");
417 	}
418 
419 	if (program.isOk() && (errorCode != GL_NO_ERROR))
420 	{
421 		log << TestLog::Message << "Fail, program status OK but a GL error was received (" << errorCode << ")." << TestLog::EndMessage;
422 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Conflicting status");
423 	}
424 	else if ((errorCode != GL_NO_ERROR) && (errorCode != GL_OUT_OF_MEMORY))
425 	{
426 		log << TestLog::Message << "Fail, expected GL_NO_ERROR or GL_OUT_OF_MEMORY, received " << errorCode << "." << TestLog::EndMessage;
427 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected GL error");
428 	}
429 }
430 
LongShaderTests(Context & testCtx)431 LongShaderTests::LongShaderTests (Context& testCtx)
432 	: TestCaseGroup(testCtx, "long_shaders", "Long shader compilation stress tests")
433 {
434 }
435 
~LongShaderTests(void)436 LongShaderTests::~LongShaderTests(void)
437 {
438 }
439 
init(void)440 void LongShaderTests::init (void)
441 {
442 	const deUint32	requireLinkOkMaxOps	= 1000;
443 
444 	const deUint32	caseOpCounts[] =
445 	{
446 		100,
447 		1000,
448 		10000,
449 		100000
450 	};
451 
452 	for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(caseOpCounts); caseNdx++)
453 	{
454 		for (int shaderTypeInt = 0; shaderTypeInt < 2; shaderTypeInt++)
455 		{
456 			const glu::ShaderType	shaderType		= (shaderTypeInt == 0) ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT;
457 			const deUint32			opCount			= caseOpCounts[caseNdx];
458 			const deUint32			flags			= (opCount <= requireLinkOkMaxOps) ? CASE_REQUIRE_LINK_STATUS_OK : 0;
459 
460 			const std::string		name			= de::toString(opCount) + "_operations_" + glu::getShaderTypeName(shaderType);
461 			const std::string		desc			= std::string("Compile ") + glu::getShaderTypeName(shaderType) + " shader with " + de::toString(opCount) + " operations";
462 
463 			LongShaderSpec			caseSpec		(shaderType, opCount);
464 
465 			addChild(new LongShaderCompileStressCase(m_context, name.c_str(), desc.c_str(), caseSpec, flags));
466 		}
467 	}
468 }
469 
470 } // Stress
471 } // gles3
472 } // deqp
473