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.
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 << "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
setupProgram(deUint32 program)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
~ConditionalCase(void)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
deinit(void)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
LoopCase(Context & context,const char * name,const char * description,LoopType type,DecisionType decisionType,bool isLoopBoundStable,bool isVertex)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
init(void)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
setupProgram(deUint32 program)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
~LoopCase(void)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
deinit(void)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
WorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)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
init(void)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:
LoopWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const767 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:
ConditionalWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const804 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:
EmptyWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)823 EmptyWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex)
824 : WorkloadReferenceCase (context, name, description, isVertex)
825 {
826 }
827
828 protected:
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const829 void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
830 {
831 dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
832 }
833 };
834
ShaderControlStatementTests(Context & context)835 ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
836 : TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
837 {
838 }
839
~ShaderControlStatementTests(void)840 ShaderControlStatementTests::~ShaderControlStatementTests (void)
841 {
842 }
843
init(void)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
966