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