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.
writeConditionalWorkload(std::ostringstream & stream,const char * resultName,const char * operandName)49 static void writeConditionalWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
50 {
51 const int numMultiplications = 64;
52
53 stream << resultName << " = ";
54
55 for (int i = 0; i < numMultiplications; i++)
56 {
57 if (i > 0)
58 stream << "*";
59
60 stream << operandName;
61 }
62
63 stream << ";";
64 }
65
66 // Writes the workload expression used in loop tests (one iteration).
writeLoopWorkload(std::ostringstream & stream,const char * resultName,const char * operandName)67 static void writeLoopWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
68 {
69 const int numMultiplications = 8;
70
71 stream << resultName << " = ";
72
73 for (int i = 0; i < numMultiplications; i++)
74 {
75 if (i > 0)
76 stream << " * ";
77
78 stream << "(" << resultName << " + " << operandName << ")";
79 }
80
81 stream << ";";
82 }
83
84 // The type of decision to be made in a conditional expression.
85 // \note In fragment cases with DECISION_ATTRIBUTE, the value in the expression will actually be a varying.
86 enum DecisionType
87 {
88 DECISION_STATIC = 0,
89 DECISION_UNIFORM,
90 DECISION_ATTRIBUTE,
91
92 DECISION_LAST
93 };
94
95 class ControlStatementCase : public ShaderPerformanceCase
96 {
97 public:
ControlStatementCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const char * name,const char * description,gls::PerfCaseType caseType)98 ControlStatementCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, gls::PerfCaseType caseType)
99 : ShaderPerformanceCase(testCtx, renderCtx, name, description, caseType)
100 {
101 }
102
init(void)103 void init (void)
104 {
105 m_testCtx.getLog() << TestLog::Message << "Using additive blending." << TestLog::EndMessage;
106 ShaderPerformanceCase::init();
107 }
108
setupRenderState(void)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
ConditionalCase(Context & context,const char * name,const char * description,DecisionType decisionType,BranchResult branchType,WorkloadDivision workloadDivision,bool isVertex)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
init(void)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
setupProgram(deUint32 program)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
~ConditionalCase(void)378 ConditionalCase::~ConditionalCase (void)
379 {
380 const glw::Functions& gl = m_renderCtx.getFunctions();
381
382 if (m_arrayBuffer != 0)
383 {
384 gl.deleteBuffers(1, &m_arrayBuffer);
385 m_arrayBuffer = 0;
386 }
387 }
388
deinit(void)389 void ConditionalCase::deinit (void)
390 {
391 const glw::Functions& gl = m_renderCtx.getFunctions();
392
393 m_comparisonValueArray.clear();
394
395 if (m_arrayBuffer != 0)
396 {
397 gl.deleteBuffers(1, &m_arrayBuffer);
398 m_arrayBuffer = 0;
399 }
400
401 ShaderPerformanceCase::deinit();
402 }
403
404 class LoopCase : public ControlStatementCase
405 {
406 public:
407 enum LoopType
408 {
409 LOOP_FOR = 0,
410 LOOP_WHILE,
411 LOOP_DO_WHILE,
412
413 LOOP_LAST
414 };
415 LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, 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
LoopCase(Context & context,const char * name,const char * description,LoopType type,DecisionType decisionType,bool isLoopBoundStable,bool isVertex)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
init(void)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
setupProgram(deUint32 program)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
~LoopCase(void)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
deinit(void)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
WorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)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
init(void)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:
LoopWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const779 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:
ConditionalWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const816 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:
EmptyWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)835 EmptyWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex)
836 : WorkloadReferenceCase (context, name, description, isVertex)
837 {
838 }
839
840 protected:
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const841 void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
842 {
843 dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
844 }
845 };
846
ShaderControlStatementTests(Context & context)847 ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
848 : TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
849 {
850 }
851
~ShaderControlStatementTests(void)852 ShaderControlStatementTests::~ShaderControlStatementTests (void)
853 {
854 }
855
init(void)856 void ShaderControlStatementTests::init (void)
857 {
858 // Conditional cases (if-else).
859
860 tcu::TestCaseGroup* ifElseGroup = new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests");
861 addChild(ifElseGroup);
862
863 for (int isFrag = 0; isFrag <= 1; isFrag++)
864 {
865 bool isVertex = isFrag == 0;
866 ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
867 ifElseGroup->addChild(vertexOrFragmentGroup);
868
869 DE_STATIC_ASSERT(DECISION_STATIC == 0);
870 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
871 {
872 const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" :
873 decisionType == (int)DECISION_UNIFORM ? "uniform" :
874 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
875 DE_NULL;
876 DE_ASSERT(decisionName != DE_NULL);
877
878 for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST; workloadDivision++)
879 {
880 const char* workloadDivisionSuffix = workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN ? "" :
881 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY ? "_with_heavier_true" :
882 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY ? "_with_heavier_false" :
883 DE_NULL;
884 DE_ASSERT(workloadDivisionSuffix != DE_NULL);
885
886 DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0);
887 for (int branchResult = (int)ConditionalCase::BRANCH_TRUE; branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++)
888 {
889 if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED)
890 continue;
891
892 const char* branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE ? "true" :
893 branchResult == (int)ConditionalCase::BRANCH_FALSE ? "false" :
894 branchResult == (int)ConditionalCase::BRANCH_MIXED ? "mixed" :
895 DE_NULL;
896 DE_ASSERT(branchResultName != DE_NULL);
897
898 string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix;
899
900 vertexOrFragmentGroup->addChild(new ConditionalCase(m_context, caseName.c_str(), "",
901 (DecisionType)decisionType, (ConditionalCase::BranchResult)branchResult,
902 (ConditionalCase::WorkloadDivision)workloadDivision, isVertex));
903 }
904 }
905 }
906
907 if (isVertex)
908 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
909 else
910 {
911 // Only fragment case with BRANCH_MIXED has an additional fract() call.
912 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex));
913 vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex));
914 }
915
916 vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex));
917 }
918
919 // Loop cases.
920
921 static const struct
922 {
923 LoopCase::LoopType type;
924 const char* name;
925 const char* description;
926 } loopGroups[] =
927 {
928 {LoopCase::LOOP_FOR, "for", "for Loop Performance Tests"},
929 {LoopCase::LOOP_WHILE, "while", "while Loop Performance Tests"},
930 {LoopCase::LOOP_DO_WHILE, "do_while", "do-while Loop Performance Tests"}
931 };
932
933 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++)
934 {
935 tcu::TestCaseGroup* currentLoopGroup = new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description);
936 addChild(currentLoopGroup);
937
938 for (int isFrag = 0; isFrag <= 1; isFrag++)
939 {
940 bool isVertex = isFrag == 0;
941 ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
942 currentLoopGroup->addChild(vertexOrFragmentGroup);
943
944 DE_STATIC_ASSERT(DECISION_STATIC == 0);
945 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
946 {
947 const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" :
948 decisionType == (int)DECISION_UNIFORM ? "uniform" :
949 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
950 DE_NULL;
951 DE_ASSERT(decisionName != DE_NULL);
952
953 if (decisionType == (int)DECISION_ATTRIBUTE)
954 {
955 vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
956 vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex));
957 }
958 else
959 vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
960
961 }
962
963 if (isVertex)
964 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
965 else
966 {
967 // Only fragment case with unstable attribute has an additional fract() call.
968 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex));
969 vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex));
970 }
971 }
972 }
973 }
974
975 } // Performance
976 } // gles3
977 } // deqp
978