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 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 << "#version 300 es\n";
201 vtx << "in highp vec4 a_position;\n"; // Position attribute.
202 vtx << "in mediump vec4 a_value0;\n"; // Input for workload calculations of "true" branch.
203 vtx << "in mediump vec4 a_value1;\n"; // Input for workload calculations of "false" branch.
204
205 frag << "#version 300 es\n";
206 frag << "layout(location = 0) out mediump vec4 o_color;\n";
207
208 // Value to be used in the conditional expression.
209 if (isAttributeCase)
210 vtx << "in mediump float a_compareValue;\n";
211 else if (isUniformCase)
212 op << "uniform mediump float u_compareValue;\n";
213
214 // Varyings.
215 if (isVertexCase)
216 {
217 vtx << "out mediump vec4 v_color;\n";
218 frag << "in mediump vec4 v_color;\n";
219 }
220 else
221 {
222 vtx << "out mediump vec4 v_value0;\n";
223 vtx << "out mediump vec4 v_value1;\n";
224 frag << "in mediump vec4 v_value0;\n";
225 frag << "in mediump vec4 v_value1;\n";
226
227 if (isAttributeCase)
228 {
229 vtx << "out mediump float v_compareValue;\n";
230 frag << "in mediump float v_compareValue;\n";
231 }
232 }
233
234 vtx << "\n";
235 vtx << "void main()\n";
236 vtx << "{\n";
237 vtx << " gl_Position = a_position;\n";
238
239 frag << "\n";
240 frag << "void main()\n";
241 frag << "{\n";
242
243 op << " mediump vec4 res;\n";
244
245 string condition;
246
247 if (isConditionMixed && !isVertexCase)
248 condition =
249 string("") + "fract(" + compareValueStr + ") < 0.5"; // Comparison result varies with high frequency.
250 else
251 condition = string("") + compareValueStr + " > 0.0";
252
253 op << " if (" << condition << ")\n";
254 op << " {\n";
255
256 op << "\t\t";
257 if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_TRUE_HEAVY)
258 writeConditionalWorkload(op, "res",
259 isVertexCase ? "a_value0" : "v_value0"); // Workload calculation for the "true" branch.
260 else
261 op << "res = " << (isVertexCase ? "a_value0" : "v_value0") << ";";
262 op << "\n";
263
264 op << " }\n";
265 op << " else\n";
266 op << " {\n";
267
268 op << "\t\t";
269 if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_FALSE_HEAVY)
270 writeConditionalWorkload(
271 op, "res", isVertexCase ? "a_value1" : "v_value1"); // Workload calculations for the "false" branch.
272 else
273 op << "res = " << (isVertexCase ? "a_value1" : "v_value1") << ";";
274 op << "\n";
275
276 op << " }\n";
277
278 if (isVertexCase)
279 {
280 // Put result to color variable.
281 vtx << " v_color = res;\n";
282 frag << " o_color = v_color;\n";
283 }
284 else
285 {
286 // Transfer inputs to fragment shader through varyings.
287 if (isAttributeCase)
288 vtx << " v_compareValue = a_compareValue;\n";
289 vtx << " v_value0 = a_value0;\n";
290 vtx << " v_value1 = a_value1;\n";
291
292 frag << " o_color = res;\n"; // Put result to color variable.
293 }
294
295 vtx << "}\n";
296 frag << "}\n";
297
298 m_vertShaderSource = vtx.str();
299 m_fragShaderSource = frag.str();
300
301 if (isAttributeCase)
302 {
303 if (!isConditionMixed)
304 {
305 // Every execution takes the same branch.
306
307 float value = isConditionTrue ? +1.0f : -1.0f;
308 m_attributes.push_back(AttribSpec("a_compareValue", Vec4(value, 0.0f, 0.0f, 0.0f),
309 Vec4(value, 0.0f, 0.0f, 0.0f), Vec4(value, 0.0f, 0.0f, 0.0f),
310 Vec4(value, 0.0f, 0.0f, 0.0f)));
311 }
312 else if (isVertexCase)
313 {
314 // Vertex case, not every execution takes the same branch.
315
316 const int numComponents = 4;
317 int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1);
318
319 // setupProgram() will later bind this array as an attribute.
320 m_comparisonValueArray.resize(numVertices * numComponents);
321
322 // Make every second vertex take the true branch, and every second the false branch.
323 for (int i = 0; i < (int)m_comparisonValueArray.size(); i++)
324 {
325 if (i % numComponents == 0)
326 m_comparisonValueArray[i] = (i / numComponents) % 2 == 0 ? +1.0f : -1.0f;
327 else
328 m_comparisonValueArray[i] = 0.0f;
329 }
330 }
331 else // isConditionMixed && !isVertexCase
332 {
333 // Fragment case, not every execution takes the same branch.
334 // \note fract(a_compareValue) < 0.5 will be true for every second column of fragments.
335
336 float minValue = 0.0f;
337 float maxValue = (float)getViewportWidth() * 0.5f;
338 m_attributes.push_back(AttribSpec("a_compareValue", Vec4(minValue, 0.0f, 0.0f, 0.0f),
339 Vec4(maxValue, 0.0f, 0.0f, 0.0f), Vec4(minValue, 0.0f, 0.0f, 0.0f),
340 Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
341 }
342 }
343
344 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),
345 Vec4(0.8f, 0.9f, 1.0f, 1.1f), Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
346
347 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),
348 Vec4(0.8f, 0.9f, 1.0f, 1.1f), Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
349
350 ControlStatementCase::init();
351 }
352
setupProgram(uint32_t program)353 void ConditionalCase::setupProgram(uint32_t program)
354 {
355 const glw::Functions &gl = m_renderCtx.getFunctions();
356
357 if (m_decisionType == DECISION_UNIFORM)
358 {
359 int location = gl.getUniformLocation(program, "u_compareValue");
360 gl.uniform1f(location, m_branchType == BRANCH_TRUE ? +1.0f : -1.0f);
361 }
362 else if (m_decisionType == DECISION_ATTRIBUTE && m_branchType == BRANCH_MIXED && m_caseType == CASETYPE_VERTEX)
363 {
364 // Setup per-vertex comparison values calculated in init().
365
366 const int numComponents = 4;
367 int compareAttribLocation = gl.getAttribLocation(program, "a_compareValue");
368
369 DE_ASSERT((int)m_comparisonValueArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
370
371 gl.genBuffers(1, &m_arrayBuffer);
372 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
373 gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_comparisonValueArray.size() * sizeof(float)),
374 &m_comparisonValueArray[0], GL_STATIC_DRAW);
375 gl.enableVertexAttribArray(compareAttribLocation);
376 gl.vertexAttribPointer(compareAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
377 }
378
379 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
380 }
381
~ConditionalCase(void)382 ConditionalCase::~ConditionalCase(void)
383 {
384 const glw::Functions &gl = m_renderCtx.getFunctions();
385
386 if (m_arrayBuffer != 0)
387 {
388 gl.deleteBuffers(1, &m_arrayBuffer);
389 m_arrayBuffer = 0;
390 }
391 }
392
deinit(void)393 void ConditionalCase::deinit(void)
394 {
395 const glw::Functions &gl = m_renderCtx.getFunctions();
396
397 m_comparisonValueArray.clear();
398
399 if (m_arrayBuffer != 0)
400 {
401 gl.deleteBuffers(1, &m_arrayBuffer);
402 m_arrayBuffer = 0;
403 }
404
405 ShaderPerformanceCase::deinit();
406 }
407
408 class LoopCase : public ControlStatementCase
409 {
410 public:
411 enum LoopType
412 {
413 LOOP_FOR = 0,
414 LOOP_WHILE,
415 LOOP_DO_WHILE,
416
417 LOOP_LAST
418 };
419 LoopCase(Context &context, const char *name, const char *description, LoopType type, DecisionType decisionType,
420 bool isLoopBoundStable, bool isVertex);
421 ~LoopCase(void);
422
423 void init(void);
424 void deinit(void);
425 void setupProgram(uint32_t program);
426
427 private:
428 DecisionType m_decisionType;
429 LoopType m_type;
430
431 bool m_isLoopBoundStable; // Whether loop bound is same in all executions.
432 vector<float> m_boundArray; // Will contain per-vertex loop bounds if using non-stable attribute in vertex case.
433 uint32_t m_arrayBuffer;
434 };
435
LoopCase(Context & context,const char * name,const char * description,LoopType type,DecisionType decisionType,bool isLoopBoundStable,bool isVertex)436 LoopCase::LoopCase(Context &context, const char *name, const char *description, LoopType type,
437 DecisionType decisionType, bool isLoopBoundStable, bool isVertex)
438 : ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description,
439 isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
440 , m_decisionType(decisionType)
441 , m_type(type)
442 , m_isLoopBoundStable(isLoopBoundStable)
443 , m_arrayBuffer(0)
444 {
445 }
446
init(void)447 void LoopCase::init(void)
448 {
449 bool isVertexCase = m_caseType == CASETYPE_VERTEX;
450
451 bool isStaticCase = m_decisionType == DECISION_STATIC;
452 bool isUniformCase = m_decisionType == DECISION_UNIFORM;
453 bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE;
454
455 DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase);
456
457 DE_ASSERT(m_type == LOOP_FOR || m_type == LOOP_WHILE || m_type == LOOP_DO_WHILE);
458
459 DE_ASSERT(isAttributeCase ||
460 m_isLoopBoundStable); // The loop bound count can vary between executions only if using attribute input.
461
462 // \note The fractional part is .5 (instead of .0) so that these can be safely used as loop bounds.
463 const float loopBound = 10.5f;
464 const float unstableBoundLow = 5.5f;
465 const float unstableBoundHigh = 15.5f;
466 static const char *loopBoundStr = "10.5";
467 static const char *unstableBoundLowStr = "5.5";
468 static const char *unstableBoundHighStr = "15.5";
469
470 const char *boundValueStr = isStaticCase ? loopBoundStr :
471 isUniformCase ? "u_bound" :
472 isVertexCase ? "a_bound" :
473 m_isLoopBoundStable ? "v_bound" :
474 "loopBound";
475
476 std::ostringstream vtx;
477 std::ostringstream frag;
478 std::ostringstream &op = isVertexCase ? vtx : frag;
479
480 vtx << "#version 300 es\n";
481 vtx << "in highp vec4 a_position;\n"; // Position attribute.
482 vtx << "in mediump vec4 a_value;\n"; // Input for workload calculations.
483
484 frag << "#version 300 es\n";
485 frag << "layout(location = 0) out mediump vec4 o_color;\n";
486
487 // Value to be used as the loop iteration count.
488 if (isAttributeCase)
489 vtx << "in mediump float a_bound;\n";
490 else if (isUniformCase)
491 op << "uniform mediump float u_bound;\n";
492
493 // Varyings.
494 if (isVertexCase)
495 {
496 vtx << "out mediump vec4 v_color;\n";
497 frag << "in mediump vec4 v_color;\n";
498 }
499 else
500 {
501 vtx << "out mediump vec4 v_value;\n";
502 frag << "in mediump vec4 v_value;\n";
503
504 if (isAttributeCase)
505 {
506 vtx << "out mediump float v_bound;\n";
507 frag << "in mediump float v_bound;\n";
508 }
509 }
510
511 vtx << "\n";
512 vtx << "void main()\n";
513 vtx << "{\n";
514 vtx << " gl_Position = a_position;\n";
515
516 frag << "\n";
517 frag << "void main()\n";
518 frag << "{\n";
519
520 op << " mediump vec4 res = vec4(0.0);\n";
521
522 if (!m_isLoopBoundStable && !isVertexCase)
523 {
524 // Choose the actual loop bound based on v_bound.
525 // \note Loop bound will vary with high frequency between fragment columns, given appropriate range for v_bound.
526 op << " mediump float loopBound = fract(v_bound) < 0.5 ? " << unstableBoundLowStr << " : "
527 << unstableBoundHighStr << ";\n";
528 }
529
530 // Start a for, while or do-while loop.
531 if (m_type == LOOP_FOR)
532 op << " for (mediump float i = 0.0; i < " << boundValueStr << "; i++)\n";
533 else
534 {
535 op << " mediump float i = 0.0;\n";
536 if (m_type == LOOP_WHILE)
537 op << " while (i < " << boundValueStr << ")\n";
538 else // LOOP_DO_WHILE
539 op << " do\n";
540 }
541 op << " {\n";
542
543 // Workload calculations inside the loop.
544 op << "\t\t";
545 writeLoopWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
546 op << "\n";
547
548 // Only "for" has counter increment in the loop head.
549 if (m_type != LOOP_FOR)
550 op << " i++;\n";
551
552 // End the loop.
553 if (m_type == LOOP_DO_WHILE)
554 op << " } while (i < " << boundValueStr << ");\n";
555 else
556 op << " }\n";
557
558 if (isVertexCase)
559 {
560 // Put result to color variable.
561 vtx << " v_color = res;\n";
562 frag << " o_color = v_color;\n";
563 }
564 else
565 {
566 // Transfer inputs to fragment shader through varyings.
567 if (isAttributeCase)
568 vtx << " v_bound = a_bound;\n";
569 vtx << " v_value = a_value;\n";
570
571 frag << " o_color = res;\n"; // Put result to color variable.
572 }
573
574 vtx << "}\n";
575 frag << "}\n";
576
577 m_vertShaderSource = vtx.str();
578 m_fragShaderSource = frag.str();
579
580 if (isAttributeCase)
581 {
582 if (m_isLoopBoundStable)
583 {
584 // Every execution has same number of iterations.
585
586 m_attributes.push_back(AttribSpec("a_bound", Vec4(loopBound, 0.0f, 0.0f, 0.0f),
587 Vec4(loopBound, 0.0f, 0.0f, 0.0f), Vec4(loopBound, 0.0f, 0.0f, 0.0f),
588 Vec4(loopBound, 0.0f, 0.0f, 0.0f)));
589 }
590 else if (isVertexCase)
591 {
592 // Vertex case, with non-constant number of iterations.
593
594 const int numComponents = 4;
595 int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1);
596
597 // setupProgram() will later bind this array as an attribute.
598 m_boundArray.resize(numVertices * numComponents);
599
600 // Vary between low and high loop bounds; they should average to loopBound however.
601 for (int i = 0; i < (int)m_boundArray.size(); i++)
602 {
603 if (i % numComponents == 0)
604 m_boundArray[i] = (i / numComponents) % 2 == 0 ? unstableBoundLow : unstableBoundHigh;
605 else
606 m_boundArray[i] = 0.0f;
607 }
608 }
609 else // !m_isLoopBoundStable && !isVertexCase
610 {
611 // Fragment case, with non-constant number of iterations.
612 // \note fract(a_bound) < 0.5 will be true for every second fragment.
613
614 float minValue = 0.0f;
615 float maxValue = (float)getViewportWidth() * 0.5f;
616 m_attributes.push_back(AttribSpec("a_bound", Vec4(minValue, 0.0f, 0.0f, 0.0f),
617 Vec4(maxValue, 0.0f, 0.0f, 0.0f), Vec4(minValue, 0.0f, 0.0f, 0.0f),
618 Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
619 }
620 }
621
622 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),
623 Vec4(0.8f, 0.9f, 1.0f, 1.1f), Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
624
625 ControlStatementCase::init();
626 }
627
setupProgram(uint32_t program)628 void LoopCase::setupProgram(uint32_t program)
629 {
630 const glw::Functions &gl = m_renderCtx.getFunctions();
631
632 if (m_decisionType == DECISION_UNIFORM)
633 {
634 const float loopBound = 10.5f;
635
636 int location = gl.getUniformLocation(program, "u_bound");
637 gl.uniform1f(location, loopBound);
638 }
639 else if (m_decisionType == DECISION_ATTRIBUTE && !m_isLoopBoundStable && m_caseType == CASETYPE_VERTEX)
640 {
641 // Setup per-vertex loop bounds calculated in init().
642
643 const int numComponents = 4;
644 int boundAttribLocation = gl.getAttribLocation(program, "a_bound");
645
646 DE_ASSERT((int)m_boundArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
647
648 gl.genBuffers(1, &m_arrayBuffer);
649 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
650 gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_boundArray.size() * sizeof(float)), &m_boundArray[0],
651 GL_STATIC_DRAW);
652 gl.enableVertexAttribArray(boundAttribLocation);
653 gl.vertexAttribPointer(boundAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
654 }
655
656 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
657 }
658
~LoopCase(void)659 LoopCase::~LoopCase(void)
660 {
661 const glw::Functions &gl = m_renderCtx.getFunctions();
662
663 if (m_arrayBuffer)
664 {
665 gl.deleteBuffers(1, &m_arrayBuffer);
666 m_arrayBuffer = 0;
667 }
668 }
669
deinit(void)670 void LoopCase::deinit(void)
671 {
672 const glw::Functions &gl = m_renderCtx.getFunctions();
673
674 m_boundArray.clear();
675
676 if (m_arrayBuffer)
677 {
678 gl.deleteBuffers(1, &m_arrayBuffer);
679 m_arrayBuffer = 0;
680 }
681
682 ShaderPerformanceCase::deinit();
683 }
684
685 // A reference case, same calculations as the actual tests but without control statements.
686 class WorkloadReferenceCase : public ControlStatementCase
687 {
688 public:
689 WorkloadReferenceCase(Context &context, const char *name, const char *description, bool isVertex);
690
691 void init(void);
692
693 protected:
694 virtual void writeWorkload(std::ostringstream &dst, const char *resultVariableName,
695 const char *inputVariableName) const = 0;
696 };
697
WorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)698 WorkloadReferenceCase::WorkloadReferenceCase(Context &context, const char *name, const char *description, bool isVertex)
699 : ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description,
700 isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
701 {
702 }
703
init(void)704 void WorkloadReferenceCase::init(void)
705 {
706 bool isVertexCase = m_caseType == CASETYPE_VERTEX;
707
708 std::ostringstream vtx;
709 std::ostringstream frag;
710 std::ostringstream &op = isVertexCase ? vtx : frag;
711
712 vtx << "#version 300 es\n";
713 vtx << "in highp vec4 a_position;\n"; // Position attribute.
714 vtx << "in mediump vec4 a_value;\n"; // Value for workload calculations.
715
716 frag << "#version 300 es\n";
717 frag << "layout(location = 0) out mediump vec4 o_color;\n";
718
719 // Varyings.
720 if (isVertexCase)
721 {
722 vtx << "out mediump vec4 v_color;\n";
723 frag << "in mediump vec4 v_color;\n";
724 }
725 else
726 {
727 vtx << "out mediump vec4 v_value;\n";
728 frag << "in mediump vec4 v_value;\n";
729 }
730
731 vtx << "\n";
732 vtx << "void main()\n";
733 vtx << "{\n";
734 vtx << " gl_Position = a_position;\n";
735
736 frag << "\n";
737 frag << "void main()\n";
738 frag << "{\n";
739
740 op << "\tmediump vec4 res;\n";
741 writeWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
742
743 if (isVertexCase)
744 {
745 // Put result to color variable.
746 vtx << " v_color = res;\n";
747 frag << " o_color = v_color;\n";
748 }
749 else
750 {
751 vtx << " v_value = a_value;\n"; // Transfer input to fragment shader through varying.
752 frag << " o_color = res;\n"; // Put result to color variable.
753 }
754
755 vtx << "}\n";
756 frag << "}\n";
757
758 m_vertShaderSource = vtx.str();
759 m_fragShaderSource = frag.str();
760
761 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),
762 Vec4(0.8f, 0.9f, 1.0f, 1.1f), Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
763
764 ControlStatementCase::init();
765 }
766
767 class LoopWorkloadReferenceCase : public WorkloadReferenceCase
768 {
769 public:
LoopWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)770 LoopWorkloadReferenceCase(Context &context, const char *name, const char *description, bool isAttributeStable,
771 bool isVertex)
772 : WorkloadReferenceCase(context, name, description, isVertex)
773 , m_isAttributeStable(isAttributeStable)
774 {
775 }
776
777 protected:
778 void writeWorkload(std::ostringstream &dst, const char *resultVariableName, const char *inputVariableName) const;
779
780 private:
781 bool m_isAttributeStable;
782 };
783
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const784 void LoopWorkloadReferenceCase::writeWorkload(std::ostringstream &dst, const char *resultVariableName,
785 const char *inputVariableName) const
786 {
787 const int loopIterations = 11;
788 bool isVertexCase = m_caseType == CASETYPE_VERTEX;
789
790 dst << "\t" << resultVariableName << " = vec4(0.0);\n";
791
792 for (int i = 0; i < loopIterations; i++)
793 {
794 dst << "\t";
795 writeLoopWorkload(dst, resultVariableName, inputVariableName);
796 dst << "\n";
797 }
798
799 if (!isVertexCase && !m_isAttributeStable)
800 {
801 // Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
802 dst << " res.x = fract(res.x);\n";
803 }
804 }
805
806 class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase
807 {
808 public:
ConditionalWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)809 ConditionalWorkloadReferenceCase(Context &context, const char *name, const char *description,
810 bool isAttributeStable, bool isVertex)
811 : WorkloadReferenceCase(context, name, description, isVertex)
812 , m_isAttributeStable(isAttributeStable)
813 {
814 }
815
816 protected:
817 void writeWorkload(std::ostringstream &dst, const char *resultVariableName, const char *inputVariableName) const;
818
819 private:
820 bool m_isAttributeStable;
821 };
822
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const823 void ConditionalWorkloadReferenceCase::writeWorkload(std::ostringstream &dst, const char *resultVariableName,
824 const char *inputVariableName) const
825 {
826 bool isVertexCase = m_caseType == CASETYPE_VERTEX;
827
828 dst << "\t";
829 writeConditionalWorkload(dst, resultVariableName, inputVariableName);
830 dst << "\n";
831
832 if (!isVertexCase && !m_isAttributeStable)
833 {
834 // Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
835 dst << " res.x = fract(res.x);\n";
836 }
837 }
838
839 // A workload reference case for e.g. a conditional case with a branch with no computation.
840 class EmptyWorkloadReferenceCase : public WorkloadReferenceCase
841 {
842 public:
EmptyWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)843 EmptyWorkloadReferenceCase(Context &context, const char *name, const char *description, bool isVertex)
844 : WorkloadReferenceCase(context, name, description, isVertex)
845 {
846 }
847
848 protected:
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const849 void writeWorkload(std::ostringstream &dst, const char *resultVariableName, const char *inputVariableName) const
850 {
851 dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
852 }
853 };
854
ShaderControlStatementTests(Context & context)855 ShaderControlStatementTests::ShaderControlStatementTests(Context &context)
856 : TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
857 {
858 }
859
~ShaderControlStatementTests(void)860 ShaderControlStatementTests::~ShaderControlStatementTests(void)
861 {
862 }
863
init(void)864 void ShaderControlStatementTests::init(void)
865 {
866 // Conditional cases (if-else).
867
868 tcu::TestCaseGroup *ifElseGroup =
869 new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests");
870 addChild(ifElseGroup);
871
872 for (int isFrag = 0; isFrag <= 1; isFrag++)
873 {
874 bool isVertex = isFrag == 0;
875 ShaderPerformanceCaseGroup *vertexOrFragmentGroup =
876 new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
877 ifElseGroup->addChild(vertexOrFragmentGroup);
878
879 DE_STATIC_ASSERT(DECISION_STATIC == 0);
880 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
881 {
882 const char *decisionName = decisionType == (int)DECISION_STATIC ? "static" :
883 decisionType == (int)DECISION_UNIFORM ? "uniform" :
884 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
885 DE_NULL;
886 DE_ASSERT(decisionName != DE_NULL);
887
888 for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST;
889 workloadDivision++)
890 {
891 const char *workloadDivisionSuffix =
892 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN ? "" :
893 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY ? "_with_heavier_true" :
894 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY ? "_with_heavier_false" :
895 DE_NULL;
896 DE_ASSERT(workloadDivisionSuffix != DE_NULL);
897
898 DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0);
899 for (int branchResult = (int)ConditionalCase::BRANCH_TRUE;
900 branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++)
901 {
902 if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED)
903 continue;
904
905 const char *branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE ? "true" :
906 branchResult == (int)ConditionalCase::BRANCH_FALSE ? "false" :
907 branchResult == (int)ConditionalCase::BRANCH_MIXED ? "mixed" :
908 DE_NULL;
909 DE_ASSERT(branchResultName != DE_NULL);
910
911 string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix;
912
913 vertexOrFragmentGroup->addChild(
914 new ConditionalCase(m_context, caseName.c_str(), "", (DecisionType)decisionType,
915 (ConditionalCase::BranchResult)branchResult,
916 (ConditionalCase::WorkloadDivision)workloadDivision, isVertex));
917 }
918 }
919 }
920
921 if (isVertex)
922 vertexOrFragmentGroup->addChild(
923 new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
924 else
925 {
926 // Only fragment case with BRANCH_MIXED has an additional fract() call.
927 vertexOrFragmentGroup->addChild(
928 new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex));
929 vertexOrFragmentGroup->addChild(
930 new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex));
931 }
932
933 vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex));
934 }
935
936 // Loop cases.
937
938 static const struct
939 {
940 LoopCase::LoopType type;
941 const char *name;
942 const char *description;
943 } loopGroups[] = {{LoopCase::LOOP_FOR, "for", "for Loop Performance Tests"},
944 {LoopCase::LOOP_WHILE, "while", "while Loop Performance Tests"},
945 {LoopCase::LOOP_DO_WHILE, "do_while", "do-while Loop Performance Tests"}};
946
947 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++)
948 {
949 tcu::TestCaseGroup *currentLoopGroup =
950 new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description);
951 addChild(currentLoopGroup);
952
953 for (int isFrag = 0; isFrag <= 1; isFrag++)
954 {
955 bool isVertex = isFrag == 0;
956 ShaderPerformanceCaseGroup *vertexOrFragmentGroup =
957 new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
958 currentLoopGroup->addChild(vertexOrFragmentGroup);
959
960 DE_STATIC_ASSERT(DECISION_STATIC == 0);
961 for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
962 {
963 const char *decisionName =
964 decisionType == (int)DECISION_STATIC ? "static" :
965 decisionType == (int)DECISION_UNIFORM ? "uniform" :
966 decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") :
967 DE_NULL;
968 DE_ASSERT(decisionName != DE_NULL);
969
970 if (decisionType == (int)DECISION_ATTRIBUTE)
971 {
972 vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(),
973 "", loopGroups[groupNdx].type,
974 (DecisionType)decisionType, true, isVertex));
975 vertexOrFragmentGroup->addChild(
976 new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "",
977 loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex));
978 }
979 else
980 vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type,
981 (DecisionType)decisionType, true, isVertex));
982 }
983
984 if (isVertex)
985 vertexOrFragmentGroup->addChild(
986 new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
987 else
988 {
989 // Only fragment case with unstable attribute has an additional fract() call.
990 vertexOrFragmentGroup->addChild(
991 new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex));
992 vertexOrFragmentGroup->addChild(
993 new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex));
994 }
995 }
996 }
997 }
998
999 } // namespace Performance
1000 } // namespace gles3
1001 } // namespace deqp
1002