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