1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 2.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 Shader control statement performance tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es2pShaderControlStatementTests.hpp" 25 #include "glsShaderPerformanceCase.hpp" 26 #include "tcuTestLog.hpp" 27 28 #include "glwEnums.hpp" 29 #include "glwFunctions.hpp" 30 31 #include <string> 32 #include <vector> 33 34 namespace deqp 35 { 36 namespace gles2 37 { 38 namespace Performance 39 { 40 41 using namespace gls; 42 using namespace glw; // GL types 43 using tcu::Vec4; 44 using tcu::TestLog; 45 using std::string; 46 using std::vector; 47 48 // Writes the workload expression used in conditional tests. 49 static void writeConditionalWorkload (std::ostringstream& stream, const char* resultName, const char* operandName) 50 { 51 const int numMultiplications = 64; 52 53 stream << resultName << " = "; 54 55 for (int i = 0; i < numMultiplications; i++) 56 { 57 if (i > 0) 58 stream << "*"; 59 60 stream << operandName; 61 } 62 63 stream << ";"; 64 } 65 66 // Writes the workload expression used in loop tests (one iteration). 67 static void writeLoopWorkload (std::ostringstream& stream, const char* resultName, const char* operandName) 68 { 69 const int numMultiplications = 8; 70 71 stream << resultName << " = "; 72 73 for (int i = 0; i < numMultiplications; i++) 74 { 75 if (i > 0) 76 stream << " * "; 77 78 stream << "(" << resultName << " + " << operandName << ")"; 79 } 80 81 stream << ";"; 82 } 83 84 // The type of decision to be made in a conditional expression. 85 // \note In fragment cases with DECISION_ATTRIBUTE, the value in the expression will actually be a varying. 86 enum DecisionType 87 { 88 DECISION_STATIC = 0, 89 DECISION_UNIFORM, 90 DECISION_ATTRIBUTE, 91 92 DECISION_LAST 93 }; 94 95 class ControlStatementCase : public ShaderPerformanceCase 96 { 97 public: 98 ControlStatementCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, gls::PerfCaseType caseType) 99 : ShaderPerformanceCase(testCtx, renderCtx, name, description, caseType) 100 { 101 } 102 103 void init (void) 104 { 105 m_testCtx.getLog() << TestLog::Message << "Using additive blending." << TestLog::EndMessage; 106 ShaderPerformanceCase::init(); 107 } 108 109 void setupRenderState (void) 110 { 111 const glw::Functions& gl = m_renderCtx.getFunctions(); 112 113 gl.enable(GL_BLEND); 114 gl.blendEquation(GL_FUNC_ADD); 115 gl.blendFunc(GL_ONE, GL_ONE); 116 } 117 }; 118 119 class ConditionalCase : public ControlStatementCase 120 { 121 public: 122 enum BranchResult 123 { 124 BRANCH_TRUE = 0, 125 BRANCH_FALSE, 126 BRANCH_MIXED, 127 128 BRANCH_LAST 129 }; 130 131 enum WorkloadDivision 132 { 133 WORKLOAD_DIVISION_EVEN = 0, //! Both true and false branches contain same amount of computation. 134 WORKLOAD_DIVISION_TRUE_HEAVY, //! True branch contains more computation. 135 WORKLOAD_DIVISION_FALSE_HEAVY, //! False branch contains more computation. 136 137 WORKLOAD_DIVISION_LAST 138 }; 139 140 ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex); 141 ~ConditionalCase (void); 142 143 void init (void); 144 void deinit (void); 145 void setupProgram (deUint32 program); 146 147 private: 148 DecisionType m_decisionType; 149 BranchResult m_branchType; 150 WorkloadDivision m_workloadDivision; 151 152 vector<float> m_comparisonValueArray; // Will contain per-vertex comparison values if using mixed branch type in vertex case. 153 deUint32 m_arrayBuffer; 154 }; 155 156 ConditionalCase::ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex) 157 : ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) 158 , m_decisionType (decisionType) 159 , m_branchType (branchType) 160 , m_workloadDivision (workloadDivision) 161 , m_arrayBuffer (0) 162 { 163 } 164 165 void ConditionalCase::init (void) 166 { 167 bool isVertexCase = m_caseType == CASETYPE_VERTEX; 168 169 bool isStaticCase = m_decisionType == DECISION_STATIC; 170 bool isUniformCase = m_decisionType == DECISION_UNIFORM; 171 bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE; 172 173 DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase); 174 175 bool isConditionTrue = m_branchType == BRANCH_TRUE; 176 bool isConditionFalse = m_branchType == BRANCH_FALSE; 177 bool isConditionMixed = m_branchType == BRANCH_MIXED; 178 179 DE_ASSERT(isConditionTrue || isConditionFalse || isConditionMixed); 180 DE_UNREF(isConditionFalse); 181 182 DE_ASSERT(isAttributeCase || !isConditionMixed); // The branch taken can vary between executions only if using attribute input. 183 184 const char* staticCompareValueStr = isConditionTrue ? "1.0" : "-1.0"; 185 const char* compareValueStr = isStaticCase ? staticCompareValueStr : 186 isUniformCase ? "u_compareValue" : 187 isVertexCase ? "a_compareValue" : 188 "v_compareValue"; 189 190 std::ostringstream vtx; 191 std::ostringstream frag; 192 std::ostringstream& op = isVertexCase ? vtx : frag; 193 194 vtx << "attribute highp vec4 a_position;\n"; // Position attribute. 195 vtx << "attribute mediump vec4 a_value0;\n"; // Input for workload calculations of "true" branch. 196 vtx << "attribute mediump vec4 a_value1;\n"; // Input for workload calculations of "false" branch. 197 198 // Value to be used in the conditional expression. 199 if (isAttributeCase) 200 vtx << "attribute mediump float a_compareValue;\n"; 201 else if (isUniformCase) 202 op << "uniform mediump float u_compareValue;\n"; 203 204 // Varyings. 205 if (isVertexCase) 206 { 207 vtx << "varying mediump vec4 v_color;\n"; 208 frag << "varying mediump vec4 v_color;\n"; 209 } 210 else 211 { 212 vtx << "varying mediump vec4 v_value0;\n"; 213 vtx << "varying mediump vec4 v_value1;\n"; 214 frag << "varying mediump vec4 v_value0;\n"; 215 frag << "varying mediump vec4 v_value1;\n"; 216 217 if (isAttributeCase) 218 { 219 vtx << "varying mediump float v_compareValue;\n"; 220 frag << "varying mediump float v_compareValue;\n"; 221 } 222 } 223 224 vtx << "\n"; 225 vtx << "void main()\n"; 226 vtx << "{\n"; 227 vtx << " gl_Position = a_position;\n"; 228 229 frag << "\n"; 230 frag << "void main()\n"; 231 frag << "{\n"; 232 233 op << " mediump vec4 res;\n"; 234 235 string condition; 236 237 if (isConditionMixed && !isVertexCase) 238 condition = string("") + "fract(" + compareValueStr + ") < 0.5"; // Comparison result varies with high frequency. 239 else 240 condition = string("") + compareValueStr + " > 0.0"; 241 242 op << " if (" << condition << ")\n"; 243 op << " {\n"; 244 245 op << "\t\t"; 246 if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_TRUE_HEAVY) 247 writeConditionalWorkload(op, "res", isVertexCase ? "a_value0" : "v_value0"); // Workload calculation for the "true" branch. 248 else 249 op << "res = " << (isVertexCase ? "a_value0" : "v_value0") << ";"; 250 op << "\n"; 251 252 op << " }\n"; 253 op << " else\n"; 254 op << " {\n"; 255 256 op << "\t\t"; 257 if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_FALSE_HEAVY) 258 writeConditionalWorkload(op, "res", isVertexCase ? "a_value1" : "v_value1"); // Workload calculations for the "false" branch. 259 else 260 op << "res = " << (isVertexCase ? "a_value1" : "v_value1") << ";"; 261 op << "\n"; 262 263 op << " }\n"; 264 265 if (isVertexCase) 266 { 267 // Put result to color variable. 268 vtx << " v_color = res;\n"; 269 frag << " gl_FragColor = v_color;\n"; 270 } 271 else 272 { 273 // Transfer inputs to fragment shader through varyings. 274 if (isAttributeCase) 275 vtx << " v_compareValue = a_compareValue;\n"; 276 vtx << " v_value0 = a_value0;\n"; 277 vtx << " v_value1 = a_value1;\n"; 278 279 frag << " gl_FragColor = res;\n"; // Put result to color variable. 280 } 281 282 vtx << "}\n"; 283 frag << "}\n"; 284 285 m_vertShaderSource = vtx.str(); 286 m_fragShaderSource = frag.str(); 287 288 if (isAttributeCase) 289 { 290 if (!isConditionMixed) 291 { 292 // Every execution takes the same branch. 293 294 float value = isConditionTrue ? +1.0f : -1.0f; 295 m_attributes.push_back(AttribSpec("a_compareValue", Vec4(value, 0.0f, 0.0f, 0.0f), 296 Vec4(value, 0.0f, 0.0f, 0.0f), 297 Vec4(value, 0.0f, 0.0f, 0.0f), 298 Vec4(value, 0.0f, 0.0f, 0.0f))); 299 } 300 else if (isVertexCase) 301 { 302 // Vertex case, not every execution takes the same branch. 303 304 const int numComponents = 4; 305 int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1); 306 307 // setupProgram() will later bind this array as an attribute. 308 m_comparisonValueArray.resize(numVertices * numComponents); 309 310 // Make every second vertex take the true branch, and every second the false branch. 311 for (int i = 0; i < (int)m_comparisonValueArray.size(); i++) 312 { 313 if (i % numComponents == 0) 314 m_comparisonValueArray[i] = (i / numComponents) % 2 == 0 ? +1.0f : -1.0f; 315 else 316 m_comparisonValueArray[i] = 0.0f; 317 } 318 } 319 else // isConditionMixed && !isVertexCase 320 { 321 // Fragment case, not every execution takes the same branch. 322 // \note fract(a_compareValue) < 0.5 will be true for every second column of fragments. 323 324 float minValue = 0.0f; 325 float maxValue = (float)getViewportWidth()*0.5f; 326 m_attributes.push_back(AttribSpec("a_compareValue", Vec4(minValue, 0.0f, 0.0f, 0.0f), 327 Vec4(maxValue, 0.0f, 0.0f, 0.0f), 328 Vec4(minValue, 0.0f, 0.0f, 0.0f), 329 Vec4(maxValue, 0.0f, 0.0f, 0.0f))); 330 } 331 } 332 333 m_attributes.push_back(AttribSpec("a_value0", Vec4(0.0f, 0.1f, 0.2f, 0.3f), 334 Vec4(0.4f, 0.5f, 0.6f, 0.7f), 335 Vec4(0.8f, 0.9f, 1.0f, 1.1f), 336 Vec4(1.2f, 1.3f, 1.4f, 1.5f))); 337 338 m_attributes.push_back(AttribSpec("a_value1", Vec4(0.0f, 0.1f, 0.2f, 0.3f), 339 Vec4(0.4f, 0.5f, 0.6f, 0.7f), 340 Vec4(0.8f, 0.9f, 1.0f, 1.1f), 341 Vec4(1.2f, 1.3f, 1.4f, 1.5f))); 342 343 ControlStatementCase::init(); 344 } 345 346 void ConditionalCase::setupProgram (deUint32 program) 347 { 348 const glw::Functions& gl = m_renderCtx.getFunctions(); 349 350 if (m_decisionType == DECISION_UNIFORM) 351 { 352 int location = gl.getUniformLocation(program, "u_compareValue"); 353 gl.uniform1f(location, m_branchType == BRANCH_TRUE ? +1.0f : -1.0f); 354 } 355 else if (m_decisionType == DECISION_ATTRIBUTE && m_branchType == BRANCH_MIXED && m_caseType == CASETYPE_VERTEX) 356 { 357 // Setup per-vertex comparison values calculated in init(). 358 359 const int numComponents = 4; 360 int compareAttribLocation = gl.getAttribLocation(program, "a_compareValue"); 361 362 DE_ASSERT((int)m_comparisonValueArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1)); 363 364 gl.genBuffers(1, &m_arrayBuffer); 365 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer); 366 gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_comparisonValueArray.size()*sizeof(float)), &m_comparisonValueArray[0], GL_STATIC_DRAW); 367 gl.enableVertexAttribArray(compareAttribLocation); 368 gl.vertexAttribPointer(compareAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL); 369 } 370 371 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state"); 372 } 373 374 ConditionalCase::~ConditionalCase (void) 375 { 376 const glw::Functions& gl = m_renderCtx.getFunctions(); 377 378 if (m_arrayBuffer != 0) 379 { 380 gl.deleteBuffers(1, &m_arrayBuffer); 381 m_arrayBuffer = 0; 382 } 383 } 384 385 void ConditionalCase::deinit (void) 386 { 387 const glw::Functions& gl = m_renderCtx.getFunctions(); 388 389 m_comparisonValueArray.clear(); 390 391 if (m_arrayBuffer != 0) 392 { 393 gl.deleteBuffers(1, &m_arrayBuffer); 394 m_arrayBuffer = 0; 395 } 396 397 ShaderPerformanceCase::deinit(); 398 } 399 400 class LoopCase : public ControlStatementCase 401 { 402 public: 403 enum LoopType 404 { 405 LOOP_FOR = 0, 406 LOOP_WHILE, 407 LOOP_DO_WHILE, 408 409 LOOP_LAST 410 }; 411 LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex); 412 ~LoopCase (void); 413 414 void init (void); 415 void deinit (void); 416 void setupProgram (deUint32 program); 417 418 private: 419 DecisionType m_decisionType; 420 LoopType m_type; 421 422 bool m_isLoopBoundStable; // Whether loop bound is same in all executions. 423 vector<float> m_boundArray; // Will contain per-vertex loop bounds if using non-stable attribute in vertex case. 424 deUint32 m_arrayBuffer; 425 }; 426 427 LoopCase::LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex) 428 : ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) 429 , m_decisionType (decisionType) 430 , m_type (type) 431 , m_isLoopBoundStable (isLoopBoundStable) 432 , m_arrayBuffer (0) 433 { 434 } 435 436 void LoopCase::init (void) 437 { 438 bool isVertexCase = m_caseType == CASETYPE_VERTEX; 439 440 bool isStaticCase = m_decisionType == DECISION_STATIC; 441 bool isUniformCase = m_decisionType == DECISION_UNIFORM; 442 bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE; 443 444 DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase); 445 446 DE_ASSERT(m_type == LOOP_FOR || 447 m_type == LOOP_WHILE || 448 m_type == LOOP_DO_WHILE); 449 450 DE_ASSERT(isAttributeCase || m_isLoopBoundStable); // The loop bound count can vary between executions only if using attribute input. 451 452 // \note The fractional part is .5 (instead of .0) so that these can be safely used as loop bounds. 453 const float loopBound = 10.5f; 454 const float unstableBoundLow = 5.5f; 455 const float unstableBoundHigh = 15.5f; 456 static const char* loopBoundStr = "10.5"; 457 static const char* unstableBoundLowStr = "5.5"; 458 static const char* unstableBoundHighStr = "15.5"; 459 460 const char* boundValueStr = isStaticCase ? loopBoundStr : 461 isUniformCase ? "u_bound" : 462 isVertexCase ? "a_bound" : 463 m_isLoopBoundStable ? "v_bound" : 464 "loopBound"; 465 466 std::ostringstream vtx; 467 std::ostringstream frag; 468 std::ostringstream& op = isVertexCase ? vtx : frag; 469 470 vtx << "attribute highp vec4 a_position;\n"; // Position attribute. 471 vtx << "attribute mediump vec4 a_value;\n"; // Input for workload calculations. 472 473 // Value to be used as the loop iteration count. 474 if (isAttributeCase) 475 vtx << "attribute mediump float a_bound;\n"; 476 else if (isUniformCase) 477 op << "uniform mediump float u_bound;\n"; 478 479 // Varyings. 480 if (isVertexCase) 481 { 482 vtx << "varying mediump vec4 v_color;\n"; 483 frag << "varying mediump vec4 v_color;\n"; 484 } 485 else 486 { 487 vtx << "varying mediump vec4 v_value;\n"; 488 frag << "varying mediump vec4 v_value;\n"; 489 490 if (isAttributeCase) 491 { 492 vtx << "varying mediump float v_bound;\n"; 493 frag << "varying mediump float v_bound;\n"; 494 } 495 } 496 497 vtx << "\n"; 498 vtx << "void main()\n"; 499 vtx << "{\n"; 500 vtx << " gl_Position = a_position;\n"; 501 502 frag << "\n"; 503 frag << "void main()\n"; 504 frag << "{\n"; 505 506 op << " mediump vec4 res = vec4(0.0);\n"; 507 508 if (!m_isLoopBoundStable && !isVertexCase) 509 { 510 // Choose the actual loop bound based on v_bound. 511 // \note Loop bound will vary with high frequency between fragment columns, given appropriate range for v_bound. 512 op << " mediump float loopBound = fract(v_bound) < 0.5 ? " << unstableBoundLowStr << " : " << unstableBoundHighStr << ";\n"; 513 } 514 515 // Start a for, while or do-while loop. 516 if (m_type == LOOP_FOR) 517 op << " for (mediump float i = 0.0; i < " << boundValueStr << "; i++)\n"; 518 else 519 { 520 op << " mediump float i = 0.0;\n"; 521 if (m_type == LOOP_WHILE) 522 op << " while (i < " << boundValueStr << ")\n"; 523 else // LOOP_DO_WHILE 524 op << " do\n"; 525 } 526 op << " {\n"; 527 528 // Workload calculations inside the loop. 529 op << "\t\t"; 530 writeLoopWorkload(op, "res", isVertexCase ? "a_value" : "v_value"); 531 op << "\n"; 532 533 // Only "for" has counter increment in the loop head. 534 if (m_type != LOOP_FOR) 535 op << " i++;\n"; 536 537 // End the loop. 538 if (m_type == LOOP_DO_WHILE) 539 op << " } while (i < " << boundValueStr << ");\n"; 540 else 541 op << " }\n"; 542 543 if (isVertexCase) 544 { 545 // Put result to color variable. 546 vtx << " v_color = res;\n"; 547 frag << " gl_FragColor = v_color;\n"; 548 } 549 else 550 { 551 // Transfer inputs to fragment shader through varyings. 552 if (isAttributeCase) 553 vtx << " v_bound = a_bound;\n"; 554 vtx << " v_value = a_value;\n"; 555 556 frag << " gl_FragColor = res;\n"; // Put result to color variable. 557 } 558 559 vtx << "}\n"; 560 frag << "}\n"; 561 562 m_vertShaderSource = vtx.str(); 563 m_fragShaderSource = frag.str(); 564 565 if (isAttributeCase) 566 { 567 if (m_isLoopBoundStable) 568 { 569 // Every execution has same number of iterations. 570 571 m_attributes.push_back(AttribSpec("a_bound", Vec4(loopBound, 0.0f, 0.0f, 0.0f), 572 Vec4(loopBound, 0.0f, 0.0f, 0.0f), 573 Vec4(loopBound, 0.0f, 0.0f, 0.0f), 574 Vec4(loopBound, 0.0f, 0.0f, 0.0f))); 575 } 576 else if (isVertexCase) 577 { 578 // Vertex case, with non-constant number of iterations. 579 580 const int numComponents = 4; 581 int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1); 582 583 // setupProgram() will later bind this array as an attribute. 584 m_boundArray.resize(numVertices * numComponents); 585 586 // Vary between low and high loop bounds; they should average to loopBound however. 587 for (int i = 0; i < (int)m_boundArray.size(); i++) 588 { 589 if (i % numComponents == 0) 590 m_boundArray[i] = (i / numComponents) % 2 == 0 ? unstableBoundLow : unstableBoundHigh; 591 else 592 m_boundArray[i] = 0.0f; 593 } 594 } 595 else // !m_isLoopBoundStable && !isVertexCase 596 { 597 // Fragment case, with non-constant number of iterations. 598 // \note fract(a_bound) < 0.5 will be true for every second fragment. 599 600 float minValue = 0.0f; 601 float maxValue = (float)getViewportWidth()*0.5f; 602 m_attributes.push_back(AttribSpec("a_bound", Vec4(minValue, 0.0f, 0.0f, 0.0f), 603 Vec4(maxValue, 0.0f, 0.0f, 0.0f), 604 Vec4(minValue, 0.0f, 0.0f, 0.0f), 605 Vec4(maxValue, 0.0f, 0.0f, 0.0f))); 606 } 607 } 608 609 m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f), 610 Vec4(0.4f, 0.5f, 0.6f, 0.7f), 611 Vec4(0.8f, 0.9f, 1.0f, 1.1f), 612 Vec4(1.2f, 1.3f, 1.4f, 1.5f))); 613 614 ControlStatementCase::init(); 615 } 616 617 void LoopCase::setupProgram (deUint32 program) 618 { 619 const glw::Functions& gl = m_renderCtx.getFunctions(); 620 621 if (m_decisionType == DECISION_UNIFORM) 622 { 623 const float loopBound = 10.5f; 624 625 int location = gl.getUniformLocation(program, "u_bound"); 626 gl.uniform1f(location, loopBound); 627 } 628 else if (m_decisionType == DECISION_ATTRIBUTE && !m_isLoopBoundStable && m_caseType == CASETYPE_VERTEX) 629 { 630 // Setup per-vertex loop bounds calculated in init(). 631 632 const int numComponents = 4; 633 int boundAttribLocation = gl.getAttribLocation(program, "a_bound"); 634 635 DE_ASSERT((int)m_boundArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1)); 636 637 gl.genBuffers(1, &m_arrayBuffer); 638 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer); 639 gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_boundArray.size()*sizeof(float)), &m_boundArray[0], GL_STATIC_DRAW); 640 gl.enableVertexAttribArray(boundAttribLocation); 641 gl.vertexAttribPointer(boundAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL); 642 } 643 644 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state"); 645 } 646 647 LoopCase::~LoopCase (void) 648 { 649 const glw::Functions& gl = m_renderCtx.getFunctions(); 650 651 if (m_arrayBuffer) 652 { 653 gl.deleteBuffers(1, &m_arrayBuffer); 654 m_arrayBuffer = 0; 655 } 656 } 657 658 void LoopCase::deinit (void) 659 { 660 const glw::Functions& gl = m_renderCtx.getFunctions(); 661 662 m_boundArray.clear(); 663 664 if (m_arrayBuffer) 665 { 666 gl.deleteBuffers(1, &m_arrayBuffer); 667 m_arrayBuffer = 0; 668 } 669 670 ShaderPerformanceCase::deinit(); 671 } 672 673 // A reference case, same calculations as the actual tests but without control statements. 674 class WorkloadReferenceCase : public ControlStatementCase 675 { 676 public: 677 WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex); 678 679 void init (void); 680 681 protected: 682 virtual void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0; 683 }; 684 685 WorkloadReferenceCase::WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex) 686 : ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) 687 { 688 } 689 690 void WorkloadReferenceCase::init (void) 691 { 692 bool isVertexCase = m_caseType == CASETYPE_VERTEX; 693 694 std::ostringstream vtx; 695 std::ostringstream frag; 696 std::ostringstream& op = isVertexCase ? vtx : frag; 697 698 vtx << "attribute highp vec4 a_position;\n"; // Position attribute. 699 vtx << "attribute mediump vec4 a_value;\n"; // Value for workload calculations. 700 701 // Varyings. 702 if (isVertexCase) 703 { 704 vtx << "varying mediump vec4 v_color;\n"; 705 frag << "varying mediump vec4 v_color;\n"; 706 } 707 else 708 { 709 vtx << "varying mediump vec4 v_value;\n"; 710 frag << "varying mediump vec4 v_value;\n"; 711 } 712 713 vtx << "\n"; 714 vtx << "void main()\n"; 715 vtx << "{\n"; 716 vtx << " gl_Position = a_position;\n"; 717 718 frag << "\n"; 719 frag << "void main()\n"; 720 frag << "{\n"; 721 722 op << "\tmediump vec4 res;\n"; 723 writeWorkload(op, "res", isVertexCase ? "a_value" : "v_value"); 724 725 if (isVertexCase) 726 { 727 // Put result to color variable. 728 vtx << " v_color = res;\n"; 729 frag << " gl_FragColor = v_color;\n"; 730 } 731 else 732 { 733 vtx << " v_value = a_value;\n"; // Transfer input to fragment shader through varying. 734 frag << " gl_FragColor = res;\n"; // Put result to color variable. 735 } 736 737 vtx << "}\n"; 738 frag << "}\n"; 739 740 m_vertShaderSource = vtx.str(); 741 m_fragShaderSource = frag.str(); 742 743 m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f), 744 Vec4(0.4f, 0.5f, 0.6f, 0.7f), 745 Vec4(0.8f, 0.9f, 1.0f, 1.1f), 746 Vec4(1.2f, 1.3f, 1.4f, 1.5f))); 747 748 ControlStatementCase::init(); 749 } 750 751 class LoopWorkloadReferenceCase : public WorkloadReferenceCase 752 { 753 public: 754 LoopWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex) 755 : WorkloadReferenceCase (context, name, description, isVertex) 756 , m_isAttributeStable (isAttributeStable) 757 { 758 } 759 760 protected: 761 void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const; 762 763 private: 764 bool m_isAttributeStable; 765 }; 766 767 void LoopWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const 768 { 769 const int loopIterations = 11; 770 bool isVertexCase = m_caseType == CASETYPE_VERTEX; 771 772 dst << "\t" << resultVariableName << " = vec4(0.0);\n"; 773 774 for (int i = 0; i < loopIterations; i++) 775 { 776 dst << "\t"; 777 writeLoopWorkload(dst, resultVariableName, inputVariableName); 778 dst << "\n"; 779 } 780 781 if (!isVertexCase && !m_isAttributeStable) 782 { 783 // Corresponds to the fract() done in a real test's fragment case with non-stable attribute. 784 dst << " res.x = fract(res.x);\n"; 785 } 786 } 787 788 class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase 789 { 790 public: 791 ConditionalWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex) 792 : WorkloadReferenceCase (context, name, description, isVertex) 793 , m_isAttributeStable (isAttributeStable) 794 { 795 } 796 797 protected: 798 void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const; 799 800 private: 801 bool m_isAttributeStable; 802 }; 803 804 void ConditionalWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const 805 { 806 bool isVertexCase = m_caseType == CASETYPE_VERTEX; 807 808 dst << "\t"; 809 writeConditionalWorkload(dst, resultVariableName, inputVariableName); 810 dst << "\n"; 811 812 if (!isVertexCase && !m_isAttributeStable) 813 { 814 // Corresponds to the fract() done in a real test's fragment case with non-stable attribute. 815 dst << " res.x = fract(res.x);\n"; 816 } 817 } 818 819 // A workload reference case for e.g. a conditional case with a branch with no computation. 820 class EmptyWorkloadReferenceCase : public WorkloadReferenceCase 821 { 822 public: 823 EmptyWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex) 824 : WorkloadReferenceCase (context, name, description, isVertex) 825 { 826 } 827 828 protected: 829 void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const 830 { 831 dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n"; 832 } 833 }; 834 835 ShaderControlStatementTests::ShaderControlStatementTests (Context& context) 836 : TestCaseGroup(context, "control_statement", "Control Statement Performance Tests") 837 { 838 } 839 840 ShaderControlStatementTests::~ShaderControlStatementTests (void) 841 { 842 } 843 844 void ShaderControlStatementTests::init (void) 845 { 846 // Conditional cases (if-else). 847 848 tcu::TestCaseGroup* ifElseGroup = new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests"); 849 addChild(ifElseGroup); 850 851 for (int isFrag = 0; isFrag <= 1; isFrag++) 852 { 853 bool isVertex = isFrag == 0; 854 ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", ""); 855 ifElseGroup->addChild(vertexOrFragmentGroup); 856 857 DE_STATIC_ASSERT(DECISION_STATIC == 0); 858 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++) 859 { 860 const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" : 861 decisionType == (int)DECISION_UNIFORM ? "uniform" : 862 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") : 863 DE_NULL; 864 DE_ASSERT(decisionName != DE_NULL); 865 866 for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST; workloadDivision++) 867 { 868 const char* workloadDivisionSuffix = workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN ? "" : 869 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY ? "_with_heavier_true" : 870 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY ? "_with_heavier_false" : 871 DE_NULL; 872 DE_ASSERT(workloadDivisionSuffix != DE_NULL); 873 874 DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0); 875 for (int branchResult = (int)ConditionalCase::BRANCH_TRUE; branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++) 876 { 877 if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED) 878 continue; 879 880 const char* branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE ? "true" : 881 branchResult == (int)ConditionalCase::BRANCH_FALSE ? "false" : 882 branchResult == (int)ConditionalCase::BRANCH_MIXED ? "mixed" : 883 DE_NULL; 884 DE_ASSERT(branchResultName != DE_NULL); 885 886 string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix; 887 888 vertexOrFragmentGroup->addChild(new ConditionalCase(m_context, caseName.c_str(), "", 889 (DecisionType)decisionType, (ConditionalCase::BranchResult)branchResult, 890 (ConditionalCase::WorkloadDivision)workloadDivision, isVertex)); 891 } 892 } 893 } 894 895 if (isVertex) 896 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex)); 897 else 898 { 899 // Only fragment case with BRANCH_MIXED has an additional fract() call. 900 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex)); 901 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex)); 902 } 903 904 vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex)); 905 } 906 907 // Loop cases. 908 909 static const struct 910 { 911 LoopCase::LoopType type; 912 const char* name; 913 const char* description; 914 } loopGroups[] = 915 { 916 {LoopCase::LOOP_FOR, "for", "for Loop Performance Tests"}, 917 {LoopCase::LOOP_WHILE, "while", "while Loop Performance Tests"}, 918 {LoopCase::LOOP_DO_WHILE, "do_while", "do-while Loop Performance Tests"} 919 }; 920 921 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++) 922 { 923 tcu::TestCaseGroup* currentLoopGroup = new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description); 924 addChild(currentLoopGroup); 925 926 for (int isFrag = 0; isFrag <= 1; isFrag++) 927 { 928 bool isVertex = isFrag == 0; 929 ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", ""); 930 currentLoopGroup->addChild(vertexOrFragmentGroup); 931 932 DE_STATIC_ASSERT(DECISION_STATIC == 0); 933 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++) 934 { 935 const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" : 936 decisionType == (int)DECISION_UNIFORM ? "uniform" : 937 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") : 938 DE_NULL; 939 DE_ASSERT(decisionName != DE_NULL); 940 941 if (decisionType == (int)DECISION_ATTRIBUTE) 942 { 943 vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex)); 944 vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex)); 945 } 946 else 947 vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex)); 948 949 } 950 951 if (isVertex) 952 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex)); 953 else 954 { 955 // Only fragment case with unstable attribute has an additional fract() call. 956 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex)); 957 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex)); 958 } 959 } 960 } 961 } 962 963 } // Performance 964 } // gles2 965 } // deqp 966