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