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