Home | History | Annotate | Download | only in stress
      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 
     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 
     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 
     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:
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    267 std::string LongShaderGenerator::getRandomVariableName (void)
    268 {
    269 	return m_rnd.choose<std::string>(m_varNames.begin(), m_varNames.end());
    270 }
    271 
    272 std::string LongShaderGenerator::getShaderOutputName (void)
    273 {
    274 	return (m_spec.shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor";
    275 }
    276 
    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 
    298 void LongShaderGenerator::addIndent (void)
    299 {
    300 	m_source += std::string(m_scopes.size(), '\t');
    301 }
    302 
    303 void LongShaderGenerator::addLine (const std::string& text)
    304 {
    305 	addIndent();
    306 	m_source += text + "\n";
    307 }
    308 
    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 
    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 
    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 
    377 LongShaderCompileStressCase::~LongShaderCompileStressCase (void)
    378 {
    379 }
    380 
    381 void LongShaderCompileStressCase::init (void)
    382 {
    383 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    384 }
    385 
    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 
    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 
    431 LongShaderTests::LongShaderTests (Context& testCtx)
    432 	: TestCaseGroup(testCtx, "long_shaders", "Long shader compilation stress tests")
    433 {
    434 }
    435 
    436 LongShaderTests::~LongShaderTests(void)
    437 {
    438 }
    439 
    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