xref: /aosp_15_r20/external/deqp/modules/gles3/stress/es3sLongShaderTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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