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