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