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