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