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 40using tcu::TestLog; 41 42namespace deqp 43{ 44namespace gles3 45{ 46namespace Stress 47{ 48 49namespace 50{ 51 52enum LongShaderCaseFlags 53{ 54 CASE_REQUIRE_LINK_STATUS_OK = 1 55}; 56 57const 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 69const 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 82const 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 92typedef std::set<std::string> ShaderScope; 93 94const char variableNamePrefixChars[] = "abcdefghijklmnopqrstuvwxyz"; 95 96class NameGenerator 97{ 98public: 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 154private: 155 std::vector<int> m_scopeIndices; 156 int m_currentScopeDepth; 157 int m_variableIndex; 158}; 159 160struct 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 179class LongShaderGenerator 180{ 181public: 182 LongShaderGenerator (de::Random& rnd, const LongShaderSpec& spec); 183 184 glu::ShaderSource getSource (void); 185 186private: 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 210LongShaderGenerator::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 217glu::ShaderSource LongShaderGenerator::getSource (void) 218{ 219 if (m_source.empty()) 220 generateSource(); 221 222 return glu::ShaderSource(m_spec.shaderType, m_source); 223} 224 225void 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 267std::string LongShaderGenerator::getRandomVariableName (void) 268{ 269 return m_rnd.choose<std::string>(m_varNames.begin(), m_varNames.end()); 270} 271 272std::string LongShaderGenerator::getShaderOutputName (void) 273{ 274 return (m_spec.shaderType == glu::SHADERTYPE_VERTEX) ? "gl_Position" : "o_fragColor"; 275} 276 277std::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 298void LongShaderGenerator::addIndent (void) 299{ 300 m_source += std::string(m_scopes.size(), '\t'); 301} 302 303void LongShaderGenerator::addLine (const std::string& text) 304{ 305 addIndent(); 306 m_source += text + "\n"; 307} 308 309void 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 330void 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 348class LongShaderCompileStressCase : public TestCase 349{ 350public: 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 360private: 361 const glu::ShaderType m_shaderType; 362 const deUint32 m_flags; 363 de::Random m_rnd; 364 LongShaderGenerator m_gen; 365}; 366 367LongShaderCompileStressCase::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 377LongShaderCompileStressCase::~LongShaderCompileStressCase (void) 378{ 379} 380 381void LongShaderCompileStressCase::init (void) 382{ 383 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 384} 385 386tcu::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 406void 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 431LongShaderTests::LongShaderTests (Context& testCtx) 432 : TestCaseGroup(testCtx, "long_shaders", "Long shader compilation stress tests") 433{ 434} 435 436LongShaderTests::~LongShaderTests(void) 437{ 438} 439 440void 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