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
40using tcu::TestLog;
41
42namespace deqp
43{
44namespace gles3
45{
46namespace Stress
47{
48
49namespace
50{
51
52enum LongShaderCaseFlags
53{
54	CASE_REQUIRE_LINK_STATUS_OK	= 1
55};
56
57const 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
69const 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
82const 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
92typedef std::set<std::string> ShaderScope;
93
94const char variableNamePrefixChars[] = "abcdefghijklmnopqrstuvwxyz";
95
96class NameGenerator
97{
98public:
99	NameGenerator (void)
100		: m_scopeIndices		(1, 0)
101		, m_currentScopeDepth	(1)
102		, m_variableIndex		(0)
103	{
104	}
105
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
118	void endScope (void)
119	{
120		DE_ASSERT(m_currentScopeDepth > 1);
121
122		m_currentScopeDepth--;
123	}
124
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
141	std::string next (void)
142	{
143		m_variableIndex++;
144
145		return makePrefix() + de::toString(m_variableIndex);
146	}
147
148	void makeNames (ShaderScope& scope, const deUint32 count)
149	{
150		for (deUint32 ndx = 0; ndx < count; ndx++)
151			scope.insert(next());
152	}
153
154private:
155	std::vector<int>	m_scopeIndices;
156	int					m_currentScopeDepth;
157	int					m_variableIndex;
158};
159
160struct LongShaderSpec
161{
162	glu::ShaderType	shaderType;
163	deUint32		opsTotal;
164
165	deUint32		variablesPerBlock;
166	deUint32		opsPerExpression;
167
168	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
179class LongShaderGenerator
180{
181public:
182								LongShaderGenerator		(de::Random& rnd, const LongShaderSpec& spec);
183
184	glu::ShaderSource			getSource				(void);
185
186private:
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
210LongShaderGenerator::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
217glu::ShaderSource LongShaderGenerator::getSource (void)
218{
219	if (m_source.empty())
220		generateSource();
221
222	return glu::ShaderSource(m_spec.shaderType, m_source);
223}
224
225void 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
267std::string LongShaderGenerator::getRandomVariableName (void)
268{
269	return m_rnd.choose<std::string>(m_varNames.begin(), m_varNames.end());
270}
271
272std::string LongShaderGenerator::getShaderOutputName (void)
273{
274	return (m_spec.shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor";
275}
276
277std::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
298void LongShaderGenerator::addIndent (void)
299{
300	m_source += std::string(m_scopes.size(), '\t');
301}
302
303void LongShaderGenerator::addLine (const std::string& text)
304{
305	addIndent();
306	m_source += text + "\n";
307}
308
309void 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
330void 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
348class LongShaderCompileStressCase : public TestCase
349{
350public:
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
360private:
361	const glu::ShaderType	m_shaderType;
362	const deUint32			m_flags;
363	de::Random				m_rnd;
364	LongShaderGenerator		m_gen;
365};
366
367LongShaderCompileStressCase::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
377LongShaderCompileStressCase::~LongShaderCompileStressCase (void)
378{
379}
380
381void LongShaderCompileStressCase::init (void)
382{
383	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
384}
385
386tcu::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
406void 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
431LongShaderTests::LongShaderTests (Context& testCtx)
432	: TestCaseGroup(testCtx, "long_shaders", "Long shader compilation stress tests")
433{
434}
435
436LongShaderTests::~LongShaderTests(void)
437{
438}
439
440void 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