xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fAtomicCounterTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 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 Basic Compute Shader Tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fAtomicCounterTests.hpp"
25 
26 #include "gluShaderProgram.hpp"
27 #include "gluObjectWrapper.hpp"
28 #include "gluRenderContext.hpp"
29 
30 #include "glwFunctions.hpp"
31 #include "glwEnums.hpp"
32 
33 #include "tcuTestLog.hpp"
34 
35 #include "deStringUtil.hpp"
36 #include "deRandom.hpp"
37 #include "deMemory.h"
38 
39 #include <vector>
40 #include <string>
41 
42 using namespace glw;
43 using tcu::TestLog;
44 
45 using std::string;
46 using std::vector;
47 
48 namespace deqp
49 {
50 namespace gles31
51 {
52 namespace Functional
53 {
54 namespace
55 {
56 
57 class AtomicCounterTest : public TestCase
58 {
59 public:
60     enum Operation
61     {
62         OPERATION_INC = (1 << 0),
63         OPERATION_DEC = (1 << 1),
64         OPERATION_GET = (1 << 2)
65     };
66 
67     enum OffsetType
68     {
69         OFFSETTYPE_NONE = 0,
70         OFFSETTYPE_BASIC,
71         OFFSETTYPE_REVERSE,
72         OFFSETTYPE_FIRST_AUTO,
73         OFFSETTYPE_DEFAULT_AUTO,
74         OFFSETTYPE_RESET_DEFAULT,
75         OFFSETTYPE_INVALID,
76         OFFSETTYPE_INVALID_OVERLAPPING,
77         OFFSETTYPE_INVALID_DEFAULT
78     };
79 
80     enum BindingType
81     {
82         BINDINGTYPE_BASIC = 0,
83         BINDINGTYPE_INVALID,
84         BINDINGTYPE_INVALID_DEFAULT
85     };
86 
87     struct TestSpec
88     {
TestSpecdeqp::gles31::Functional::__anon85d56dab0111::AtomicCounterTest::TestSpec89         TestSpec(void)
90             : atomicCounterCount(0)
91             , operations((Operation)0)
92             , callCount(0)
93             , useBranches(false)
94             , threadCount(0)
95             , offsetType(OFFSETTYPE_NONE)
96             , bindingType(BINDINGTYPE_BASIC)
97         {
98         }
99 
100         int atomicCounterCount;
101         Operation operations;
102         int callCount;
103         bool useBranches;
104         int threadCount;
105         OffsetType offsetType;
106         BindingType bindingType;
107     };
108 
109     AtomicCounterTest(Context &context, const char *name, const char *description, const TestSpec &spec);
110     ~AtomicCounterTest(void);
111 
112     void init(void);
113     void deinit(void);
114     IterateResult iterate(void);
115 
116 private:
117     const TestSpec m_spec;
118 
119     bool checkAndLogCounterValues(TestLog &log, const vector<uint32_t> &counters) const;
120     bool checkAndLogCallValues(TestLog &log, const vector<uint32_t> &increments, const vector<uint32_t> &decrements,
121                                const vector<uint32_t> &preGets, const vector<uint32_t> &postGets,
122                                const vector<uint32_t> &gets) const;
123     void splitBuffer(const vector<uint32_t> &buffer, vector<uint32_t> &increments, vector<uint32_t> &decrements,
124                      vector<uint32_t> &preGets, vector<uint32_t> &postGets, vector<uint32_t> &gets) const;
getInitialValue(void) const125     uint32_t getInitialValue(void) const
126     {
127         return m_spec.callCount * m_spec.threadCount + 1;
128     }
129 
130     static string generateShaderSource(const TestSpec &spec);
131     static void getCountersValues(vector<uint32_t> &counterValues, const vector<uint32_t> &values, int ndx,
132                                   int counterCount);
133     static bool checkRange(TestLog &log, const vector<uint32_t> &values, const vector<uint32_t> &min,
134                            const vector<uint32_t> &max);
135     static bool checkUniquenessAndLinearity(TestLog &log, const vector<uint32_t> &values);
136     static bool checkPath(const vector<uint32_t> &increments, const vector<uint32_t> &decrements, int initialValue,
137                           const TestSpec &spec);
138 
139     int getOperationCount(void) const;
140 
141     AtomicCounterTest &operator=(const AtomicCounterTest &);
142     AtomicCounterTest(const AtomicCounterTest &);
143 };
144 
getOperationCount(void) const145 int AtomicCounterTest::getOperationCount(void) const
146 {
147     int count = 0;
148 
149     if (m_spec.operations & OPERATION_INC)
150         count++;
151 
152     if (m_spec.operations & OPERATION_DEC)
153         count++;
154 
155     if (m_spec.operations == OPERATION_GET)
156         count++;
157     else if (m_spec.operations & OPERATION_GET)
158         count += 2;
159 
160     return count;
161 }
162 
AtomicCounterTest(Context & context,const char * name,const char * description,const TestSpec & spec)163 AtomicCounterTest::AtomicCounterTest(Context &context, const char *name, const char *description, const TestSpec &spec)
164     : TestCase(context, name, description)
165     , m_spec(spec)
166 {
167 }
168 
~AtomicCounterTest(void)169 AtomicCounterTest::~AtomicCounterTest(void)
170 {
171 }
172 
init(void)173 void AtomicCounterTest::init(void)
174 {
175 }
176 
deinit(void)177 void AtomicCounterTest::deinit(void)
178 {
179 }
180 
generateShaderSource(const TestSpec & spec)181 string AtomicCounterTest::generateShaderSource(const TestSpec &spec)
182 {
183     std::ostringstream src;
184 
185     src << "#version 310 es\n"
186         << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n";
187 
188     {
189         bool wroteLayout = false;
190 
191         switch (spec.bindingType)
192         {
193         case BINDINGTYPE_INVALID_DEFAULT:
194             src << "layout(binding=10000";
195             wroteLayout = true;
196             break;
197 
198         default:
199             // Do nothing
200             break;
201         }
202 
203         switch (spec.offsetType)
204         {
205         case OFFSETTYPE_DEFAULT_AUTO:
206             if (!wroteLayout)
207                 src << "layout(binding=0, ";
208             else
209                 src << ", ";
210 
211             src << "offset=4";
212             wroteLayout = true;
213             break;
214 
215         case OFFSETTYPE_RESET_DEFAULT:
216             DE_ASSERT(spec.atomicCounterCount > 2);
217 
218             if (!wroteLayout)
219                 src << "layout(binding=0, ";
220             else
221                 src << ", ";
222 
223             src << "offset=" << (4 * spec.atomicCounterCount / 2);
224             wroteLayout = true;
225             break;
226 
227         case OFFSETTYPE_INVALID_DEFAULT:
228             if (!wroteLayout)
229                 src << "layout(binding=0, ";
230             else
231                 src << ", ";
232 
233             src << "offset=1";
234             wroteLayout = true;
235             break;
236 
237         default:
238             // Do nothing
239             break;
240         }
241 
242         if (wroteLayout)
243             src << ") uniform atomic_uint;\n";
244     }
245 
246     src << "layout(binding = 1, std430) buffer Output {\n";
247 
248     if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
249         src << "    uint preGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
250 
251     if ((spec.operations & OPERATION_INC) != 0)
252         src << "    uint increment[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
253 
254     if ((spec.operations & OPERATION_DEC) != 0)
255         src << "    uint decrement[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
256 
257     if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
258         src << "    uint postGet[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
259 
260     if (spec.operations == OPERATION_GET)
261         src << "    uint get[" << spec.threadCount * spec.atomicCounterCount * spec.callCount << "];\n";
262 
263     src << "} sb_in;\n\n";
264 
265     for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
266     {
267         bool layoutStarted = false;
268 
269         if (spec.offsetType == OFFSETTYPE_RESET_DEFAULT && counterNdx == spec.atomicCounterCount / 2)
270             src << "layout(binding=0, offset=0) uniform atomic_uint;\n";
271 
272         switch (spec.bindingType)
273         {
274         case BINDINGTYPE_BASIC:
275             layoutStarted = true;
276             src << "layout(binding=0";
277             break;
278 
279         case BINDINGTYPE_INVALID:
280             layoutStarted = true;
281             src << "layout(binding=10000";
282             break;
283 
284         case BINDINGTYPE_INVALID_DEFAULT:
285             // Nothing
286             break;
287 
288         default:
289             DE_ASSERT(false);
290         }
291 
292         switch (spec.offsetType)
293         {
294         case OFFSETTYPE_NONE:
295             if (layoutStarted)
296                 src << ") ";
297 
298             src << "uniform atomic_uint counter" << counterNdx << ";\n";
299 
300             break;
301 
302         case OFFSETTYPE_BASIC:
303             if (!layoutStarted)
304                 src << "layout(";
305             else
306                 src << ", ";
307 
308             src << "offset=" << (counterNdx * 4) << ") uniform atomic_uint counter" << counterNdx << ";\n";
309 
310             break;
311 
312         case OFFSETTYPE_INVALID_DEFAULT:
313             if (layoutStarted)
314                 src << ") ";
315 
316             src << "uniform atomic_uint counter" << counterNdx << ";\n";
317 
318             break;
319 
320         case OFFSETTYPE_INVALID:
321             if (!layoutStarted)
322                 src << "layout(";
323             else
324                 src << ", ";
325 
326             src << "offset=" << (1 + counterNdx * 2) << ") uniform atomic_uint counter" << counterNdx << ";\n";
327 
328             break;
329 
330         case OFFSETTYPE_INVALID_OVERLAPPING:
331             if (!layoutStarted)
332                 src << "layout(";
333             else
334                 src << ", ";
335 
336             src << "offset=0) uniform atomic_uint counter" << counterNdx << ";\n";
337 
338             break;
339 
340         case OFFSETTYPE_REVERSE:
341             if (!layoutStarted)
342                 src << "layout(";
343             else
344                 src << ", ";
345 
346             src << "offset=" << (spec.atomicCounterCount - counterNdx - 1) * 4 << ") uniform atomic_uint counter"
347                 << (spec.atomicCounterCount - counterNdx - 1) << ";\n";
348 
349             break;
350 
351         case OFFSETTYPE_FIRST_AUTO:
352             DE_ASSERT(spec.atomicCounterCount > 2);
353 
354             if (counterNdx + 1 == spec.atomicCounterCount)
355             {
356                 if (!layoutStarted)
357                     src << "layout(";
358                 else
359                     src << ", ";
360 
361                 src << "offset=0) uniform atomic_uint counter0;\n";
362             }
363             else if (counterNdx == 0)
364             {
365                 if (!layoutStarted)
366                     src << "layout(";
367                 else
368                     src << ", ";
369 
370                 src << "offset=4) uniform atomic_uint counter1;\n";
371             }
372             else
373             {
374                 if (layoutStarted)
375                     src << ") ";
376 
377                 src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
378             }
379 
380             break;
381 
382         case OFFSETTYPE_DEFAULT_AUTO:
383             if (counterNdx + 1 == spec.atomicCounterCount)
384             {
385                 if (!layoutStarted)
386                     src << "layout(";
387                 else
388                     src << ", ";
389 
390                 src << "offset=0) uniform atomic_uint counter0;\n";
391             }
392             else
393             {
394                 if (layoutStarted)
395                     src << ") ";
396 
397                 src << "uniform atomic_uint counter" << (counterNdx + 1) << ";\n";
398             }
399 
400             break;
401 
402         case OFFSETTYPE_RESET_DEFAULT:
403             if (layoutStarted)
404                 src << ") ";
405 
406             if (counterNdx < spec.atomicCounterCount / 2)
407                 src << "uniform atomic_uint counter" << (counterNdx + spec.atomicCounterCount / 2) << ";\n";
408             else
409                 src << "uniform atomic_uint counter" << (counterNdx - spec.atomicCounterCount / 2) << ";\n";
410 
411             break;
412 
413         default:
414             DE_ASSERT(false);
415         }
416     }
417 
418     src << "\n"
419         << "void main (void)\n"
420         << "{\n";
421 
422     if (spec.callCount > 1)
423         src << "\tfor (uint i = 0u; i < " << spec.callCount << "u; i++)\n";
424 
425     src << "\t{\n"
426         << "\t\tuint id = (gl_GlobalInvocationID.x";
427 
428     if (spec.callCount > 1)
429         src << " * " << spec.callCount << "u";
430 
431     if (spec.callCount > 1)
432         src << " + i)";
433     else
434         src << ")";
435 
436     if (spec.atomicCounterCount > 1)
437         src << " * " << spec.atomicCounterCount << "u";
438 
439     src << ";\n";
440 
441     for (int counterNdx = 0; counterNdx < spec.atomicCounterCount; counterNdx++)
442     {
443         if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
444             src << "\t\tsb_in.preGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
445 
446         if (spec.useBranches &&
447             ((spec.operations & (OPERATION_INC | OPERATION_DEC)) == (OPERATION_INC | OPERATION_DEC)))
448         {
449             src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
450                 << "\t\t{\n"
451                 << "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter" << counterNdx
452                 << ");\n"
453                 << "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
454                 << "\t\t}\n"
455                 << "\t\telse\n"
456                 << "\t\t{\n"
457                 << "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter" << counterNdx
458                 << ") + 1u;\n"
459                 << "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
460                 << "\t\t}\n";
461         }
462         else
463         {
464             if ((spec.operations & OPERATION_INC) != 0)
465             {
466                 if (spec.useBranches)
467                 {
468                     src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "")
469                         << ") % 2u) == 0u)\n"
470                         << "\t\t{\n"
471                         << "\t\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter"
472                         << counterNdx << ");\n"
473                         << "\t\t}\n"
474                         << "\t\telse\n"
475                         << "\t\t{\n"
476                         << "\t\t\tsb_in.increment[id + " << counterNdx << "u] = uint(-1);\n"
477                         << "\t\t}\n";
478                 }
479                 else
480                     src << "\t\tsb_in.increment[id + " << counterNdx << "u] = atomicCounterIncrement(counter"
481                         << counterNdx << ");\n";
482             }
483 
484             if ((spec.operations & OPERATION_DEC) != 0)
485             {
486                 if (spec.useBranches)
487                 {
488                     src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "")
489                         << ") % 2u) == 0u)\n"
490                         << "\t\t{\n"
491                         << "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter"
492                         << counterNdx << ") + 1u;\n"
493                         << "\t\t}\n"
494                         << "\t\telse\n"
495                         << "\t\t{\n"
496                         << "\t\t\tsb_in.decrement[id + " << counterNdx << "u] = uint(-1);\n"
497                         << "\t\t}\n";
498                 }
499                 else
500                     src << "\t\tsb_in.decrement[id + " << counterNdx << "u] = atomicCounterDecrement(counter"
501                         << counterNdx << ") + 1u;\n";
502             }
503         }
504 
505         if ((spec.operations & OPERATION_GET) != 0 && spec.operations != OPERATION_GET)
506             src << "\t\tsb_in.postGet[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
507 
508         if ((spec.operations == OPERATION_GET) != 0)
509         {
510             if (spec.useBranches)
511             {
512                 src << "\t\tif (((gl_GlobalInvocationID.x" << (spec.callCount > 1 ? " + i" : "") << ") % 2u) == 0u)\n"
513                     << "\t\t{\n"
514                     << "\t\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n"
515                     << "\t\t}\n"
516                     << "\t\telse\n"
517                     << "\t\t{\n"
518                     << "\t\t\tsb_in.get[id + " << counterNdx << "u] = uint(-1);\n"
519                     << "\t\t}\n";
520             }
521             else
522                 src << "\t\tsb_in.get[id + " << counterNdx << "u] = atomicCounter(counter" << counterNdx << ");\n";
523         }
524     }
525 
526     src << "\t}\n"
527         << "}\n";
528 
529     return src.str();
530 }
531 
checkAndLogCounterValues(TestLog & log,const vector<uint32_t> & counters) const532 bool AtomicCounterTest::checkAndLogCounterValues(TestLog &log, const vector<uint32_t> &counters) const
533 {
534     tcu::ScopedLogSection counterSection(log, "Counter info",
535                                          "Show initial value, current value and expected value of each counter.");
536     bool isOk = true;
537 
538     // Check that atomic counters have sensible results
539     for (int counterNdx = 0; counterNdx < (int)counters.size(); counterNdx++)
540     {
541         const uint32_t value        = counters[counterNdx];
542         const uint32_t initialValue = getInitialValue();
543         uint32_t expectedValue      = (uint32_t)-1;
544 
545         if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) == 0)
546             expectedValue = initialValue + (m_spec.useBranches ? m_spec.threadCount * m_spec.callCount -
547                                                                      m_spec.threadCount * m_spec.callCount / 2 :
548                                                                  m_spec.threadCount * m_spec.callCount);
549 
550         if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) != 0)
551             expectedValue = initialValue - (m_spec.useBranches ? m_spec.threadCount * m_spec.callCount -
552                                                                      m_spec.threadCount * m_spec.callCount / 2 :
553                                                                  m_spec.threadCount * m_spec.callCount);
554 
555         if ((m_spec.operations & OPERATION_INC) != 0 && (m_spec.operations & OPERATION_DEC) != 0)
556             expectedValue = initialValue +
557                             (m_spec.useBranches ?
558                                  m_spec.threadCount * m_spec.callCount - m_spec.threadCount * m_spec.callCount / 2 :
559                                  0) -
560                             (m_spec.useBranches ? m_spec.threadCount * m_spec.callCount / 2 : 0);
561 
562         if ((m_spec.operations & OPERATION_INC) == 0 && (m_spec.operations & OPERATION_DEC) == 0)
563             expectedValue = initialValue;
564 
565         log << TestLog::Message << "atomic_uint counter" << counterNdx << " initial value: " << initialValue
566             << ", value: " << value << ", expected: " << expectedValue << (value == expectedValue ? "" : ", failed!")
567             << TestLog::EndMessage;
568 
569         if (value != expectedValue)
570             isOk = false;
571     }
572 
573     return isOk;
574 }
575 
splitBuffer(const vector<uint32_t> & buffer,vector<uint32_t> & increments,vector<uint32_t> & decrements,vector<uint32_t> & preGets,vector<uint32_t> & postGets,vector<uint32_t> & gets) const576 void AtomicCounterTest::splitBuffer(const vector<uint32_t> &buffer, vector<uint32_t> &increments,
577                                     vector<uint32_t> &decrements, vector<uint32_t> &preGets, vector<uint32_t> &postGets,
578                                     vector<uint32_t> &gets) const
579 {
580     const int bufferValueCount = m_spec.callCount * m_spec.threadCount * m_spec.atomicCounterCount;
581 
582     int firstPreGet  = -1;
583     int firstPostGet = -1;
584     int firstGet     = -1;
585     int firstInc     = -1;
586     int firstDec     = -1;
587 
588     increments.clear();
589     decrements.clear();
590     preGets.clear();
591     postGets.clear();
592     gets.clear();
593 
594     if (m_spec.operations == OPERATION_GET)
595         firstGet = 0;
596     else if (m_spec.operations == OPERATION_INC)
597         firstInc = 0;
598     else if (m_spec.operations == OPERATION_DEC)
599         firstDec = 0;
600     else if (m_spec.operations == (OPERATION_GET | OPERATION_INC))
601     {
602         firstPreGet  = 0;
603         firstInc     = bufferValueCount;
604         firstPostGet = bufferValueCount * 2;
605     }
606     else if (m_spec.operations == (OPERATION_GET | OPERATION_DEC))
607     {
608         firstPreGet  = 0;
609         firstDec     = bufferValueCount;
610         firstPostGet = bufferValueCount * 2;
611     }
612     else if (m_spec.operations == (OPERATION_GET | OPERATION_DEC | OPERATION_INC))
613     {
614         firstPreGet  = 0;
615         firstInc     = bufferValueCount;
616         firstDec     = bufferValueCount * 2;
617         firstPostGet = bufferValueCount * 3;
618     }
619     else if (m_spec.operations == (OPERATION_DEC | OPERATION_INC))
620     {
621         firstInc = 0;
622         firstDec = bufferValueCount;
623     }
624     else
625         DE_ASSERT(false);
626 
627     for (int threadNdx = 0; threadNdx < m_spec.threadCount; threadNdx++)
628     {
629         for (int callNdx = 0; callNdx < m_spec.callCount; callNdx++)
630         {
631             for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
632             {
633                 const int id = ((threadNdx * m_spec.callCount) + callNdx) * m_spec.atomicCounterCount + counterNdx;
634 
635                 if (firstInc != -1)
636                     increments.push_back(buffer[firstInc + id]);
637 
638                 if (firstDec != -1)
639                     decrements.push_back(buffer[firstDec + id]);
640 
641                 if (firstPreGet != -1)
642                     preGets.push_back(buffer[firstPreGet + id]);
643 
644                 if (firstPostGet != -1)
645                     postGets.push_back(buffer[firstPostGet + id]);
646 
647                 if (firstGet != -1)
648                     gets.push_back(buffer[firstGet + id]);
649             }
650         }
651     }
652 }
653 
getCountersValues(vector<uint32_t> & counterValues,const vector<uint32_t> & values,int ndx,int counterCount)654 void AtomicCounterTest::getCountersValues(vector<uint32_t> &counterValues, const vector<uint32_t> &values, int ndx,
655                                           int counterCount)
656 {
657     counterValues.resize(values.size() / counterCount, 0);
658 
659     DE_ASSERT(values.size() % counterCount == 0);
660 
661     for (int valueNdx = 0; valueNdx < (int)counterValues.size(); valueNdx++)
662         counterValues[valueNdx] = values[valueNdx * counterCount + ndx];
663 }
664 
checkRange(TestLog & log,const vector<uint32_t> & values,const vector<uint32_t> & min,const vector<uint32_t> & max)665 bool AtomicCounterTest::checkRange(TestLog &log, const vector<uint32_t> &values, const vector<uint32_t> &min,
666                                    const vector<uint32_t> &max)
667 {
668     int failedCount = 0;
669 
670     DE_ASSERT(values.size() == min.size());
671     DE_ASSERT(values.size() == max.size());
672 
673     for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
674     {
675         if (values[valueNdx] != (uint32_t)-1)
676         {
677             if (!deInRange32(values[valueNdx], min[valueNdx], max[valueNdx]))
678             {
679                 if (failedCount < 20)
680                     log << TestLog::Message << "Value " << values[valueNdx] << " not in range [" << min[valueNdx]
681                         << ", " << max[valueNdx] << "]." << TestLog::EndMessage;
682                 failedCount++;
683             }
684         }
685     }
686 
687     if (failedCount > 20)
688         log << TestLog::Message << "Number of values not in range: " << failedCount << ", displaying first 20 values."
689             << TestLog::EndMessage;
690 
691     return failedCount == 0;
692 }
693 
checkUniquenessAndLinearity(TestLog & log,const vector<uint32_t> & values)694 bool AtomicCounterTest::checkUniquenessAndLinearity(TestLog &log, const vector<uint32_t> &values)
695 {
696     vector<uint32_t> counts;
697     int failedCount   = 0;
698     uint32_t minValue = (uint32_t)-1;
699     uint32_t maxValue = 0;
700 
701     DE_ASSERT(!values.empty());
702 
703     for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
704     {
705         if (values[valueNdx] != (uint32_t)-1)
706         {
707             minValue = std::min(minValue, values[valueNdx]);
708             maxValue = std::max(maxValue, values[valueNdx]);
709         }
710     }
711 
712     counts.resize(maxValue - minValue + 1, 0);
713 
714     for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++)
715     {
716         if (values[valueNdx] != (uint32_t)-1)
717             counts[values[valueNdx] - minValue]++;
718     }
719 
720     for (int countNdx = 0; countNdx < (int)counts.size(); countNdx++)
721     {
722         if (counts[countNdx] != 1)
723         {
724             if (failedCount < 20)
725                 log << TestLog::Message << "Value " << (minValue + countNdx) << " is not unique. Returned "
726                     << counts[countNdx] << " times." << TestLog::EndMessage;
727 
728             failedCount++;
729         }
730     }
731 
732     if (failedCount > 20)
733         log << TestLog::Message << "Number of values not unique: " << failedCount << ", displaying first 20 values."
734             << TestLog::EndMessage;
735 
736     return failedCount == 0;
737 }
738 
checkPath(const vector<uint32_t> & increments,const vector<uint32_t> & decrements,int initialValue,const TestSpec & spec)739 bool AtomicCounterTest::checkPath(const vector<uint32_t> &increments, const vector<uint32_t> &decrements,
740                                   int initialValue, const TestSpec &spec)
741 {
742     const uint32_t lastValue =
743         initialValue +
744         (spec.useBranches ? spec.threadCount * spec.callCount - spec.threadCount * spec.callCount / 2 : 0) -
745         (spec.useBranches ? spec.threadCount * spec.callCount / 2 : 0);
746     bool isOk = true;
747 
748     vector<uint32_t> incrementCounts;
749     vector<uint32_t> decrementCounts;
750 
751     uint32_t minValue = 0xFFFFFFFFu;
752     uint32_t maxValue = 0;
753 
754     for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
755     {
756         if (increments[valueNdx] != (uint32_t)-1)
757         {
758             minValue = std::min(minValue, increments[valueNdx]);
759             maxValue = std::max(maxValue, increments[valueNdx]);
760         }
761     }
762 
763     for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
764     {
765         if (decrements[valueNdx] != (uint32_t)-1)
766         {
767             minValue = std::min(minValue, decrements[valueNdx]);
768             maxValue = std::max(maxValue, decrements[valueNdx]);
769         }
770     }
771 
772     minValue = std::min(minValue, (uint32_t)initialValue);
773     maxValue = std::max(maxValue, (uint32_t)initialValue);
774 
775     incrementCounts.resize(maxValue - minValue + 1, 0);
776     decrementCounts.resize(maxValue - minValue + 1, 0);
777 
778     for (int valueNdx = 0; valueNdx < (int)increments.size(); valueNdx++)
779     {
780         if (increments[valueNdx] != (uint32_t)-1)
781             incrementCounts[increments[valueNdx] - minValue]++;
782     }
783 
784     for (int valueNdx = 0; valueNdx < (int)decrements.size(); valueNdx++)
785     {
786         if (decrements[valueNdx] != (uint32_t)-1)
787             decrementCounts[decrements[valueNdx] - minValue]++;
788     }
789 
790     int pos = initialValue - minValue;
791 
792     while (incrementCounts[pos] + decrementCounts[pos] != 0)
793     {
794         if (incrementCounts[pos] > 0 && pos >= (int)(lastValue - minValue))
795         {
796             // If can increment and incrementation would move us away from result value, increment
797             incrementCounts[pos]--;
798             pos++;
799         }
800         else if (decrementCounts[pos] > 0)
801         {
802             // If can, decrement
803             decrementCounts[pos]--;
804             pos--;
805         }
806         else if (incrementCounts[pos] > 0)
807         {
808             // If increment moves closer to result value and can't decrement, increment
809             incrementCounts[pos]--;
810             pos++;
811         }
812         else
813             DE_ASSERT(false);
814 
815         if (pos < 0 || pos >= (int)incrementCounts.size())
816             break;
817     }
818 
819     if (minValue + pos != lastValue)
820         isOk = false;
821 
822     for (int valueNdx = 0; valueNdx < (int)incrementCounts.size(); valueNdx++)
823     {
824         if (incrementCounts[valueNdx] != 0)
825             isOk = false;
826     }
827 
828     for (int valueNdx = 0; valueNdx < (int)decrementCounts.size(); valueNdx++)
829     {
830         if (decrementCounts[valueNdx] != 0)
831             isOk = false;
832     }
833 
834     return isOk;
835 }
836 
checkAndLogCallValues(TestLog & log,const vector<uint32_t> & increments,const vector<uint32_t> & decrements,const vector<uint32_t> & preGets,const vector<uint32_t> & postGets,const vector<uint32_t> & gets) const837 bool AtomicCounterTest::checkAndLogCallValues(TestLog &log, const vector<uint32_t> &increments,
838                                               const vector<uint32_t> &decrements, const vector<uint32_t> &preGets,
839                                               const vector<uint32_t> &postGets, const vector<uint32_t> &gets) const
840 {
841     bool isOk = true;
842 
843     for (int counterNdx = 0; counterNdx < m_spec.atomicCounterCount; counterNdx++)
844     {
845         vector<uint32_t> counterIncrements;
846         vector<uint32_t> counterDecrements;
847         vector<uint32_t> counterPreGets;
848         vector<uint32_t> counterPostGets;
849         vector<uint32_t> counterGets;
850 
851         getCountersValues(counterIncrements, increments, counterNdx, m_spec.atomicCounterCount);
852         getCountersValues(counterDecrements, decrements, counterNdx, m_spec.atomicCounterCount);
853         getCountersValues(counterPreGets, preGets, counterNdx, m_spec.atomicCounterCount);
854         getCountersValues(counterPostGets, postGets, counterNdx, m_spec.atomicCounterCount);
855         getCountersValues(counterGets, gets, counterNdx, m_spec.atomicCounterCount);
856 
857         if (m_spec.operations == OPERATION_GET)
858         {
859             tcu::ScopedLogSection valueCheck(
860                 log, ("counter" + de::toString(counterNdx) + " value check").c_str(),
861                 ("Check that counter" + de::toString(counterNdx) + " values haven't changed.").c_str());
862             int changedValues = 0;
863 
864             for (int valueNdx = 0; valueNdx < (int)gets.size(); valueNdx++)
865             {
866                 if ((!m_spec.useBranches || gets[valueNdx] != (uint32_t)-1) && gets[valueNdx] != getInitialValue())
867                 {
868                     if (changedValues < 20)
869                         log << TestLog::Message << "atomicCounter(counter" << counterNdx << ") returned "
870                             << gets[valueNdx] << " expected " << getInitialValue() << TestLog::EndMessage;
871                     isOk = false;
872                     changedValues++;
873                 }
874             }
875 
876             if (changedValues == 0)
877                 log << TestLog::Message << "All values returned by atomicCounter(counter" << counterNdx
878                     << ") match initial value " << getInitialValue() << "." << TestLog::EndMessage;
879             else if (changedValues > 20)
880                 log << TestLog::Message << "Total number of invalid values returned by atomicCounter(counter"
881                     << counterNdx << ") " << changedValues << " displaying first 20 values." << TestLog::EndMessage;
882         }
883         else if ((m_spec.operations & (OPERATION_INC | OPERATION_DEC)) == (OPERATION_INC | OPERATION_DEC))
884         {
885             tcu::ScopedLogSection valueCheck(log, ("counter" + de::toString(counterNdx) + " path check").c_str(),
886                                              ("Check that there is order in which counter" + de::toString(counterNdx) +
887                                               " increments and decrements could have happened.")
888                                                  .c_str());
889             if (!checkPath(counterIncrements, counterDecrements, getInitialValue(), m_spec))
890             {
891                 isOk = false;
892                 log << TestLog::Message << "No possible order of calls to atomicCounterIncrement(counter" << counterNdx
893                     << ") and atomicCounterDecrement(counter" << counterNdx << ") found." << TestLog::EndMessage;
894             }
895             else
896                 log << TestLog::Message << "Found possible order of calls to atomicCounterIncrement(counter"
897                     << counterNdx << ") and atomicCounterDecrement(counter" << counterNdx << ")."
898                     << TestLog::EndMessage;
899         }
900         else if ((m_spec.operations & OPERATION_INC) != 0)
901         {
902             {
903                 tcu::ScopedLogSection uniquenesCheck(
904                     log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(),
905                     ("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.")
906                         .c_str());
907 
908                 if (!checkUniquenessAndLinearity(log, counterIncrements))
909                 {
910                     isOk = false;
911                     log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
912                         << ") returned non unique values." << TestLog::EndMessage;
913                 }
914                 else
915                     log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
916                         << ") returned only unique values." << TestLog::EndMessage;
917             }
918 
919             if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
920             {
921                 tcu::ScopedLogSection uniquenesCheck(
922                     log, ("counter" + de::toString(counterNdx) + " check range").c_str(),
923                     ("Check that counter" + de::toString(counterNdx) +
924                      " returned only values values between previous and next atomicCounter(counter" +
925                      de::toString(counterNdx) + ").")
926                         .c_str());
927 
928                 if (!checkRange(log, counterIncrements, counterPreGets, counterPostGets))
929                 {
930                     isOk = false;
931                     log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
932                         << ") returned value that is not between previous and next call to atomicCounter(counter"
933                         << counterNdx << ")." << TestLog::EndMessage;
934                 }
935                 else
936                     log << TestLog::Message << "atomicCounterIncrement(counter" << counterNdx
937                         << ") returned only values between previous and next call to atomicCounter(counter"
938                         << counterNdx << ")." << TestLog::EndMessage;
939             }
940         }
941         else if ((m_spec.operations & OPERATION_DEC) != 0)
942         {
943             {
944                 tcu::ScopedLogSection uniquenesCheck(
945                     log, ("counter" + de::toString(counterNdx) + " check uniqueness and linearity").c_str(),
946                     ("Check that counter" + de::toString(counterNdx) + " returned only unique and linear values.")
947                         .c_str());
948 
949                 if (!checkUniquenessAndLinearity(log, counterDecrements))
950                 {
951                     isOk = false;
952                     log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
953                         << ") returned non unique values." << TestLog::EndMessage;
954                 }
955                 else
956                     log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
957                         << ") returned only unique values." << TestLog::EndMessage;
958             }
959 
960             if (isOk && ((m_spec.operations & OPERATION_GET) != 0))
961             {
962                 tcu::ScopedLogSection uniquenesCheck(
963                     log, ("counter" + de::toString(counterNdx) + " check range").c_str(),
964                     ("Check that counter" + de::toString(counterNdx) +
965                      " returned only values values between previous and next atomicCounter(counter" +
966                      de::toString(counterNdx) + ".")
967                         .c_str());
968 
969                 if (!checkRange(log, counterDecrements, counterPostGets, counterPreGets))
970                 {
971                     isOk = false;
972                     log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
973                         << ") returned value that is not between previous and next call to atomicCounter(counter"
974                         << counterNdx << ")." << TestLog::EndMessage;
975                 }
976                 else
977                     log << TestLog::Message << "atomicCounterDecrement(counter" << counterNdx
978                         << ") returned only values between previous and next call to atomicCounter(counter"
979                         << counterNdx << ")." << TestLog::EndMessage;
980             }
981         }
982     }
983 
984     return isOk;
985 }
986 
iterate(void)987 TestCase::IterateResult AtomicCounterTest::iterate(void)
988 {
989     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
990     TestLog &log             = m_testCtx.getLog();
991     const glu::Buffer counterBuffer(m_context.getRenderContext());
992     const glu::Buffer outputBuffer(m_context.getRenderContext());
993     const glu::ShaderProgram program(m_context.getRenderContext(),
994                                      glu::ProgramSources()
995                                          << glu::ShaderSource(glu::SHADERTYPE_COMPUTE, generateShaderSource(m_spec)));
996 
997     const int32_t counterBufferSize = m_spec.atomicCounterCount * 4;
998     const int32_t ssoSize = m_spec.atomicCounterCount * m_spec.callCount * m_spec.threadCount * 4 * getOperationCount();
999 
1000     log << program;
1001 
1002     if (m_spec.offsetType == OFFSETTYPE_INVALID || m_spec.offsetType == OFFSETTYPE_INVALID_DEFAULT ||
1003         m_spec.bindingType == BINDINGTYPE_INVALID || m_spec.bindingType == BINDINGTYPE_INVALID_DEFAULT ||
1004         m_spec.offsetType == OFFSETTYPE_INVALID_OVERLAPPING)
1005     {
1006         if (program.isOk())
1007         {
1008             log << TestLog::Message << "Expected program to fail, but compilation passed." << TestLog::EndMessage;
1009             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile succeeded");
1010             return STOP;
1011         }
1012         else
1013         {
1014             log << TestLog::Message << "Compilation failed as expected." << TestLog::EndMessage;
1015             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Compile failed");
1016             return STOP;
1017         }
1018     }
1019     else if (!program.isOk())
1020     {
1021         log << TestLog::Message << "Compile failed." << TestLog::EndMessage;
1022         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
1023         return STOP;
1024     }
1025 
1026     gl.useProgram(program.getProgram());
1027     GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()");
1028 
1029     // Create output buffer
1030     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
1031     gl.bufferData(GL_SHADER_STORAGE_BUFFER, ssoSize, NULL, GL_STATIC_DRAW);
1032     GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create output buffer");
1033 
1034     // Create atomic counter buffer
1035     {
1036         vector<uint32_t> data(m_spec.atomicCounterCount, getInitialValue());
1037         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
1038         gl.bufferData(GL_SHADER_STORAGE_BUFFER, counterBufferSize, &(data[0]), GL_STATIC_DRAW);
1039         GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create buffer for atomic counters");
1040     }
1041 
1042     // Bind output buffer
1043     gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, *outputBuffer);
1044     GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup output buffer");
1045 
1046     // Bind atomic counter buffer
1047     gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, *counterBuffer);
1048     GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup atomic counter buffer");
1049 
1050     // Dispath compute
1051     gl.dispatchCompute(m_spec.threadCount, 1, 1);
1052     GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute()");
1053 
1054     gl.finish();
1055     GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()");
1056 
1057     vector<uint32_t> output(ssoSize / 4, 0);
1058     vector<uint32_t> counters(m_spec.atomicCounterCount, 0);
1059 
1060     // Read back output buffer
1061     {
1062         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
1063         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1064 
1065         void *ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(output.size() * sizeof(uint32_t)),
1066                                       GL_MAP_READ_BIT);
1067         GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1068 
1069         deMemcpy(&(output[0]), ptr, (int)output.size() * sizeof(uint32_t));
1070 
1071         if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
1072         {
1073             GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1074             TCU_CHECK_MSG(false, "Mapped buffer corrupted");
1075         }
1076 
1077         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1078         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1079     }
1080 
1081     // Read back counter buffer
1082     {
1083         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, *counterBuffer);
1084         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1085 
1086         void *ptr = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)(counters.size() * sizeof(uint32_t)),
1087                                       GL_MAP_READ_BIT);
1088         GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
1089 
1090         deMemcpy(&(counters[0]), ptr, (int)counters.size() * sizeof(uint32_t));
1091 
1092         if (!gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER))
1093         {
1094             GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer()");
1095             TCU_CHECK_MSG(false, "Mapped buffer corrupted");
1096         }
1097 
1098         gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1099         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()");
1100     }
1101 
1102     bool isOk = true;
1103 
1104     if (!checkAndLogCounterValues(log, counters))
1105         isOk = false;
1106 
1107     {
1108         vector<uint32_t> increments;
1109         vector<uint32_t> decrements;
1110         vector<uint32_t> preGets;
1111         vector<uint32_t> postGets;
1112         vector<uint32_t> gets;
1113 
1114         splitBuffer(output, increments, decrements, preGets, postGets, gets);
1115 
1116         if (!checkAndLogCallValues(log, increments, decrements, preGets, postGets, gets))
1117             isOk = false;
1118     }
1119 
1120     if (isOk)
1121         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1122     else
1123         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1124 
1125     return STOP;
1126 }
1127 
specToTestName(const AtomicCounterTest::TestSpec & spec)1128 string specToTestName(const AtomicCounterTest::TestSpec &spec)
1129 {
1130     std::ostringstream stream;
1131 
1132     stream << spec.atomicCounterCount << (spec.atomicCounterCount == 1 ? "_counter" : "_counters");
1133     stream << "_" << spec.callCount << (spec.callCount == 1 ? "_call" : "_calls");
1134     stream << "_" << spec.threadCount << (spec.threadCount == 1 ? "_thread" : "_threads");
1135 
1136     return stream.str();
1137 }
1138 
specToTestDescription(const AtomicCounterTest::TestSpec & spec)1139 string specToTestDescription(const AtomicCounterTest::TestSpec &spec)
1140 {
1141     std::ostringstream stream;
1142     bool firstOperation = 0;
1143 
1144     stream << "Test ";
1145 
1146     if ((spec.operations & AtomicCounterTest::OPERATION_GET) != 0)
1147     {
1148         stream << "atomicCounter()";
1149         firstOperation = false;
1150     }
1151 
1152     if ((spec.operations & AtomicCounterTest::OPERATION_INC) != 0)
1153     {
1154         if (!firstOperation)
1155             stream << ", ";
1156 
1157         stream << " atomicCounterIncrement()";
1158         firstOperation = false;
1159     }
1160 
1161     if ((spec.operations & AtomicCounterTest::OPERATION_DEC) != 0)
1162     {
1163         if (!firstOperation)
1164             stream << ", ";
1165 
1166         stream << " atomicCounterDecrement()";
1167         firstOperation = false;
1168     }
1169 
1170     stream << " calls with ";
1171 
1172     if (spec.useBranches)
1173         stream << " branches, ";
1174 
1175     stream << spec.atomicCounterCount << " atomic counters, " << spec.callCount << " calls and " << spec.threadCount
1176            << " threads.";
1177 
1178     return stream.str();
1179 }
1180 
operationToName(const AtomicCounterTest::Operation & operations,bool useBranch)1181 string operationToName(const AtomicCounterTest::Operation &operations, bool useBranch)
1182 {
1183     std::ostringstream stream;
1184     bool first = true;
1185 
1186     if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
1187     {
1188         stream << "get";
1189         first = false;
1190     }
1191 
1192     if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
1193     {
1194         if (!first)
1195             stream << "_";
1196 
1197         stream << "inc";
1198         first = false;
1199     }
1200 
1201     if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
1202     {
1203         if (!first)
1204             stream << "_";
1205 
1206         stream << "dec";
1207         first = false;
1208     }
1209 
1210     if (useBranch)
1211         stream << "_branch";
1212 
1213     return stream.str();
1214 }
1215 
operationToDescription(const AtomicCounterTest::Operation & operations,bool useBranch)1216 string operationToDescription(const AtomicCounterTest::Operation &operations, bool useBranch)
1217 {
1218     std::ostringstream stream;
1219     bool firstOperation = 0;
1220 
1221     stream << "Test ";
1222 
1223     if ((operations & AtomicCounterTest::OPERATION_GET) != 0)
1224     {
1225         stream << "atomicCounter()";
1226         firstOperation = false;
1227     }
1228 
1229     if ((operations & AtomicCounterTest::OPERATION_INC) != 0)
1230     {
1231         if (!firstOperation)
1232             stream << ", ";
1233 
1234         stream << " atomicCounterIncrement()";
1235         firstOperation = false;
1236     }
1237 
1238     if ((operations & AtomicCounterTest::OPERATION_DEC) != 0)
1239     {
1240         if (!firstOperation)
1241             stream << ", ";
1242 
1243         stream << " atomicCounterDecrement()";
1244         firstOperation = false;
1245     }
1246 
1247     if (useBranch)
1248         stream << " calls with branches.";
1249     else
1250         stream << ".";
1251 
1252     return stream.str();
1253 }
1254 
layoutTypesToName(const AtomicCounterTest::BindingType & bindingType,const AtomicCounterTest::OffsetType & offsetType)1255 string layoutTypesToName(const AtomicCounterTest::BindingType &bindingType,
1256                          const AtomicCounterTest::OffsetType &offsetType)
1257 {
1258     std::ostringstream stream;
1259 
1260     switch (bindingType)
1261     {
1262     case AtomicCounterTest::BINDINGTYPE_BASIC:
1263         // Nothing
1264         break;
1265 
1266     case AtomicCounterTest::BINDINGTYPE_INVALID:
1267         stream << "invalid_binding";
1268         break;
1269 
1270     default:
1271         DE_ASSERT(false);
1272     }
1273 
1274     if (bindingType != AtomicCounterTest::BINDINGTYPE_BASIC && offsetType != AtomicCounterTest::OFFSETTYPE_NONE)
1275         stream << "_";
1276 
1277     switch (offsetType)
1278     {
1279     case AtomicCounterTest::OFFSETTYPE_BASIC:
1280         stream << "basic_offset";
1281         break;
1282 
1283     case AtomicCounterTest::OFFSETTYPE_REVERSE:
1284         stream << "reverse_offset";
1285         break;
1286 
1287     case AtomicCounterTest::OFFSETTYPE_INVALID:
1288         stream << "invalid_offset";
1289         break;
1290 
1291     case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
1292         stream << "first_offset_set";
1293         break;
1294 
1295     case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
1296         stream << "default_offset_set";
1297         break;
1298 
1299     case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
1300         stream << "reset_default_offset";
1301         break;
1302 
1303     case AtomicCounterTest::OFFSETTYPE_NONE:
1304         // Do nothing
1305         break;
1306 
1307     default:
1308         DE_ASSERT(false);
1309     }
1310 
1311     return stream.str();
1312 }
1313 
layoutTypesToDesc(const AtomicCounterTest::BindingType & bindingType,const AtomicCounterTest::OffsetType & offsetType)1314 string layoutTypesToDesc(const AtomicCounterTest::BindingType &bindingType,
1315                          const AtomicCounterTest::OffsetType &offsetType)
1316 {
1317     std::ostringstream stream;
1318 
1319     switch (bindingType)
1320     {
1321     case AtomicCounterTest::BINDINGTYPE_BASIC:
1322         stream << "Test using atomic counters with explicit layout bindings and";
1323         break;
1324 
1325     case AtomicCounterTest::BINDINGTYPE_INVALID:
1326         stream << "Test using atomic counters with invalid explicit layout bindings and";
1327         break;
1328 
1329     case AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT:
1330         stream << "Test using atomic counters with invalid default layout binding and";
1331         break;
1332 
1333     default:
1334         DE_ASSERT(false);
1335     }
1336 
1337     switch (offsetType)
1338     {
1339     case AtomicCounterTest::OFFSETTYPE_NONE:
1340         stream << " no explicit offsets.";
1341         break;
1342 
1343     case AtomicCounterTest::OFFSETTYPE_BASIC:
1344         stream << "explicit continuos offsets.";
1345         break;
1346 
1347     case AtomicCounterTest::OFFSETTYPE_REVERSE:
1348         stream << "reversed explicit offsets.";
1349         break;
1350 
1351     case AtomicCounterTest::OFFSETTYPE_INVALID:
1352         stream << "invalid explicit offsets.";
1353         break;
1354 
1355     case AtomicCounterTest::OFFSETTYPE_FIRST_AUTO:
1356         stream << "only first counter with explicit offset.";
1357         break;
1358 
1359     case AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO:
1360         stream << "default offset.";
1361         break;
1362 
1363     case AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT:
1364         stream << "default offset specified twice.";
1365         break;
1366 
1367     default:
1368         DE_ASSERT(false);
1369     }
1370 
1371     return stream.str();
1372 }
1373 
1374 } // namespace
1375 
AtomicCounterTests(Context & context)1376 AtomicCounterTests::AtomicCounterTests(Context &context)
1377     : TestCaseGroup(context, "atomic_counter", "Atomic counter tests")
1378 {
1379     // Runtime use tests
1380     {
1381         const int counterCounts[] = {1, 4, 8};
1382 
1383         const int callCounts[] = {1, 5, 100};
1384 
1385         const int threadCounts[] = {1, 10, 5000};
1386 
1387         const AtomicCounterTest::Operation operations[] = {
1388             AtomicCounterTest::OPERATION_GET,
1389             AtomicCounterTest::OPERATION_INC,
1390             AtomicCounterTest::OPERATION_DEC,
1391 
1392             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_GET),
1393             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC | AtomicCounterTest::OPERATION_GET),
1394 
1395             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC),
1396             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC |
1397                                            AtomicCounterTest::OPERATION_GET)};
1398 
1399         for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
1400         {
1401             const AtomicCounterTest::Operation operation = operations[operationNdx];
1402 
1403             for (int branch = 0; branch < 2; branch++)
1404             {
1405                 const bool useBranch = (branch == 1);
1406 
1407                 TestCaseGroup *operationGroup =
1408                     new TestCaseGroup(m_context, operationToName(operation, useBranch).c_str(),
1409                                       operationToDescription(operation, useBranch).c_str());
1410 
1411                 for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
1412                 {
1413                     const int counterCount = counterCounts[counterCountNdx];
1414 
1415                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
1416                     {
1417                         const int callCount = callCounts[callCountNdx];
1418 
1419                         for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts);
1420                              threadCountNdx++)
1421                         {
1422                             const int threadCount = threadCounts[threadCountNdx];
1423 
1424                             if (threadCount * callCount * counterCount > 10000)
1425                                 continue;
1426 
1427                             if (useBranch && threadCount * callCount == 1)
1428                                 continue;
1429 
1430                             AtomicCounterTest::TestSpec spec;
1431 
1432                             spec.atomicCounterCount = counterCount;
1433                             spec.operations         = operation;
1434                             spec.callCount          = callCount;
1435                             spec.useBranches        = useBranch;
1436                             spec.threadCount        = threadCount;
1437                             spec.bindingType        = AtomicCounterTest::BINDINGTYPE_BASIC;
1438                             spec.offsetType         = AtomicCounterTest::OFFSETTYPE_NONE;
1439 
1440                             operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(),
1441                                                                            specToTestDescription(spec).c_str(), spec));
1442                         }
1443                     }
1444                 }
1445 
1446                 addChild(operationGroup);
1447             }
1448         }
1449     }
1450 
1451     {
1452         TestCaseGroup *layoutGroup = new TestCaseGroup(m_context, "layout", "Layout qualifier tests.");
1453 
1454         const int counterCounts[] = {1, 8};
1455         const int callCounts[]    = {1, 5};
1456         const int threadCounts[]  = {1, 1000};
1457 
1458         const AtomicCounterTest::Operation operations[] = {
1459             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_GET),
1460             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_DEC | AtomicCounterTest::OPERATION_GET),
1461             (AtomicCounterTest::Operation)(AtomicCounterTest::OPERATION_INC | AtomicCounterTest::OPERATION_DEC)};
1462 
1463         const AtomicCounterTest::OffsetType offsetTypes[] = {
1464             AtomicCounterTest::OFFSETTYPE_REVERSE, AtomicCounterTest::OFFSETTYPE_FIRST_AUTO,
1465             AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO, AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT};
1466 
1467         for (int offsetTypeNdx = 0; offsetTypeNdx < DE_LENGTH_OF_ARRAY(offsetTypes); offsetTypeNdx++)
1468         {
1469             const AtomicCounterTest::OffsetType offsetType = offsetTypes[offsetTypeNdx];
1470 
1471             TestCaseGroup *layoutQualifierGroup = new TestCaseGroup(
1472                 m_context, layoutTypesToName(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str(),
1473                 layoutTypesToDesc(AtomicCounterTest::BINDINGTYPE_BASIC, offsetType).c_str());
1474 
1475             for (int operationNdx = 0; operationNdx < DE_LENGTH_OF_ARRAY(operations); operationNdx++)
1476             {
1477                 const AtomicCounterTest::Operation operation = operations[operationNdx];
1478 
1479                 TestCaseGroup *operationGroup = new TestCaseGroup(m_context, operationToName(operation, false).c_str(),
1480                                                                   operationToDescription(operation, false).c_str());
1481 
1482                 for (int counterCountNdx = 0; counterCountNdx < DE_LENGTH_OF_ARRAY(counterCounts); counterCountNdx++)
1483                 {
1484                     const int counterCount = counterCounts[counterCountNdx];
1485 
1486                     if (offsetType == AtomicCounterTest::OFFSETTYPE_FIRST_AUTO && counterCount < 3)
1487                         continue;
1488 
1489                     if (offsetType == AtomicCounterTest::OFFSETTYPE_DEFAULT_AUTO && counterCount < 2)
1490                         continue;
1491 
1492                     if (offsetType == AtomicCounterTest::OFFSETTYPE_RESET_DEFAULT && counterCount < 2)
1493                         continue;
1494 
1495                     if (offsetType == AtomicCounterTest::OFFSETTYPE_REVERSE && counterCount < 2)
1496                         continue;
1497 
1498                     for (int callCountNdx = 0; callCountNdx < DE_LENGTH_OF_ARRAY(callCounts); callCountNdx++)
1499                     {
1500                         const int callCount = callCounts[callCountNdx];
1501 
1502                         for (int threadCountNdx = 0; threadCountNdx < DE_LENGTH_OF_ARRAY(threadCounts);
1503                              threadCountNdx++)
1504                         {
1505                             const int threadCount = threadCounts[threadCountNdx];
1506 
1507                             AtomicCounterTest::TestSpec spec;
1508 
1509                             spec.atomicCounterCount = counterCount;
1510                             spec.operations         = operation;
1511                             spec.callCount          = callCount;
1512                             spec.useBranches        = false;
1513                             spec.threadCount        = threadCount;
1514                             spec.bindingType        = AtomicCounterTest::BINDINGTYPE_BASIC;
1515                             spec.offsetType         = offsetType;
1516 
1517                             operationGroup->addChild(new AtomicCounterTest(m_context, specToTestName(spec).c_str(),
1518                                                                            specToTestDescription(spec).c_str(), spec));
1519                         }
1520                     }
1521                 }
1522                 layoutQualifierGroup->addChild(operationGroup);
1523             }
1524             layoutGroup->addChild(layoutQualifierGroup);
1525         }
1526 
1527         {
1528             TestCaseGroup *invalidGroup = new TestCaseGroup(m_context, "invalid", "Test invalid layouts");
1529 
1530             {
1531                 AtomicCounterTest::TestSpec spec;
1532 
1533                 spec.atomicCounterCount = 1;
1534                 spec.operations         = AtomicCounterTest::OPERATION_INC;
1535                 spec.callCount          = 1;
1536                 spec.useBranches        = false;
1537                 spec.threadCount        = 1;
1538                 spec.bindingType        = AtomicCounterTest::BINDINGTYPE_INVALID;
1539                 spec.offsetType         = AtomicCounterTest::OFFSETTYPE_NONE;
1540 
1541                 invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_binding",
1542                                                              "Test layout qualifiers with invalid binding.", spec));
1543             }
1544 
1545             {
1546                 AtomicCounterTest::TestSpec spec;
1547 
1548                 spec.atomicCounterCount = 1;
1549                 spec.operations         = AtomicCounterTest::OPERATION_INC;
1550                 spec.callCount          = 1;
1551                 spec.useBranches        = false;
1552                 spec.threadCount        = 1;
1553                 spec.bindingType        = AtomicCounterTest::BINDINGTYPE_INVALID_DEFAULT;
1554                 spec.offsetType         = AtomicCounterTest::OFFSETTYPE_NONE;
1555 
1556                 invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_default_binding",
1557                                                              "Test layout qualifiers with invalid default binding.",
1558                                                              spec));
1559             }
1560 
1561             {
1562                 AtomicCounterTest::TestSpec spec;
1563 
1564                 spec.atomicCounterCount = 1;
1565                 spec.operations         = AtomicCounterTest::OPERATION_INC;
1566                 spec.callCount          = 1;
1567                 spec.useBranches        = false;
1568                 spec.threadCount        = 1;
1569                 spec.bindingType        = AtomicCounterTest::BINDINGTYPE_BASIC;
1570                 spec.offsetType         = AtomicCounterTest::OFFSETTYPE_INVALID;
1571 
1572                 invalidGroup->addChild(new AtomicCounterTest(
1573                     m_context, "invalid_offset_align", "Test layout qualifiers with invalid alignment offset.", spec));
1574             }
1575 
1576             {
1577                 AtomicCounterTest::TestSpec spec;
1578 
1579                 spec.atomicCounterCount = 2;
1580                 spec.operations         = AtomicCounterTest::OPERATION_INC;
1581                 spec.callCount          = 1;
1582                 spec.useBranches        = false;
1583                 spec.threadCount        = 1;
1584                 spec.bindingType        = AtomicCounterTest::BINDINGTYPE_BASIC;
1585                 spec.offsetType         = AtomicCounterTest::OFFSETTYPE_INVALID_OVERLAPPING;
1586 
1587                 invalidGroup->addChild(new AtomicCounterTest(m_context, "invalid_offset_overlap",
1588                                                              "Test layout qualifiers with invalid overlapping offset.",
1589                                                              spec));
1590             }
1591 
1592             {
1593                 AtomicCounterTest::TestSpec spec;
1594 
1595                 spec.atomicCounterCount = 1;
1596                 spec.operations         = AtomicCounterTest::OPERATION_INC;
1597                 spec.callCount          = 1;
1598                 spec.useBranches        = false;
1599                 spec.threadCount        = 1;
1600                 spec.bindingType        = AtomicCounterTest::BINDINGTYPE_BASIC;
1601                 spec.offsetType         = AtomicCounterTest::OFFSETTYPE_INVALID_DEFAULT;
1602 
1603                 invalidGroup->addChild(new AtomicCounterTest(
1604                     m_context, "invalid_default_offset", "Test layout qualifiers with invalid default offset.", spec));
1605             }
1606 
1607             layoutGroup->addChild(invalidGroup);
1608         }
1609 
1610         addChild(layoutGroup);
1611     }
1612 }
1613 
~AtomicCounterTests(void)1614 AtomicCounterTests::~AtomicCounterTests(void)
1615 {
1616 }
1617 
init(void)1618 void AtomicCounterTests::init(void)
1619 {
1620 }
1621 
1622 } // namespace Functional
1623 } // namespace gles31
1624 } // namespace deqp
1625