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