1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2021 The Khronos Group Inc.
6  * Copyright (c) 2021 Google LLC.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Shared memory model layout tests.
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMemoryModelSharedLayout.hpp"
26 #include "vktMemoryModelSharedLayoutCase.hpp"
27 
28 #include "tcuCommandLine.hpp"
29 #include "tcuTestLog.hpp"
30 #include "deRandom.hpp"
31 #include "deStringUtil.hpp"
32 #include "vktTestCaseUtil.hpp"
33 #include "vkMemUtil.hpp"
34 
35 namespace vkt
36 {
37 namespace MemoryModel
38 {
39 using std::string;
40 using std::vector;
41 
42 namespace
43 {
44 
45 enum FeatureBits
46 {
47     FEATURE_VECTORS          = (1 << 0),
48     FEATURE_MATRICES         = (1 << 1),
49     FEATURE_ARRAYS           = (1 << 2),
50     FEATURE_STRUCTS          = (1 << 3),
51     FEATURE_UNUSED_VARS      = (1 << 4),
52     FEATURE_UNUSED_MEMBERS   = (1 << 5),
53     FEATURE_ARRAYS_OF_ARRAYS = (1 << 6),
54     FEATURE_16BIT_TYPES      = (1 << 7),
55     FEATURE_8BIT_TYPES       = (1 << 8),
56 };
57 
58 /*--------------------------------------------------------------------*//*!
59  * \brief Generates names for shared memory structs and their members.
60  * \param first The first character of the alphabet.
61  * \param last The last character of the alphabet.
62  * \param ndx The index of the name in the alphabet.
63  *
64  * If the index lies within the range [1, (last-first)+1], returns
65  * the character represented by the ASCII code 'first + ndx - 1'
66  * as a string.
67  *
68  * E.g. if "first" is 'a', "last" 'z' and "ndx" is 1, returns a. If "ndx"
69  * is 2, returns b and so forth.
70  *
71  * If "ndx" is greater than the range, the function keeps dividing it by
72  * the alphabet length until the index is within the range. In each iteration,
73  * the name is prefixed with the ASCII character represented by the modulo
74  * of the index.
75  *
76  * E.g. if "first" is 'a', "last" 'z' and "ndx" is 28, returns ab. If "ndx"
77  * is 702, returns aaa and so forth.
78  *//*--------------------------------------------------------------------*/
genName(char first,char last,int ndx)79 string genName(char first, char last, int ndx)
80 {
81     string str;
82     int alphabetLen = static_cast<int>(last) - static_cast<int>(first) + 1;
83 
84     while (ndx > 0)
85     {
86         const int asciiCode = static_cast<int>(first) + ((ndx - 1) % alphabetLen);
87         str.insert(str.begin(), static_cast<char>(asciiCode));
88         ndx = ((ndx - 1) / alphabetLen);
89     }
90 
91     return str;
92 }
93 
createRandomCaseGroup(tcu::TestCaseGroup * parentGroup,tcu::TestContext & testCtx,const char * groupName,const uint32_t features,const int numCases,uint32_t baseSeed)94 void createRandomCaseGroup(tcu::TestCaseGroup *parentGroup, tcu::TestContext &testCtx, const char *groupName,
95                            const uint32_t features, const int numCases, uint32_t baseSeed)
96 {
97     tcu::TestCaseGroup *group = new tcu::TestCaseGroup(testCtx, groupName);
98     parentGroup->addChild(group);
99 
100     baseSeed += static_cast<uint32_t>(testCtx.getCommandLine().getBaseSeed());
101 
102     for (int i = 0; i < numCases; i++)
103         group->addChild(new RandomSharedLayoutCase(testCtx, de::toString(i).c_str(), features,
104                                                    static_cast<uint32_t>(i + baseSeed)));
105 }
106 } // namespace
107 
RandomSharedLayoutCase(tcu::TestContext & testCtx,const char * name,uint32_t features,uint32_t seed)108 RandomSharedLayoutCase::RandomSharedLayoutCase(tcu::TestContext &testCtx, const char *name, uint32_t features,
109                                                uint32_t seed)
110     : SharedLayoutCase(testCtx, name)
111     , m_features(features)
112     , m_maxArrayLength((features & FEATURE_ARRAYS) ? 3 : 0)
113     , m_seed(seed)
114 {
115     de::Random rnd(m_seed);
116 
117     m_interface.enable16BitTypes(features & FEATURE_16BIT_TYPES);
118     m_interface.enable8BitTypes(features & FEATURE_8BIT_TYPES);
119 
120     for (int i = 0; i < rnd.getInt(1, m_maxSharedObjects); i++)
121         generateSharedMemoryObject(rnd);
122 
123     init();
124 }
125 
126 /*--------------------------------------------------------------------*//*!
127  * \brief Creates definitions for shared memory structs.
128  * \param rnd Random value generator used for deciding the type of the variable.
129  *
130  * Creates definitions for shared memory structs. Each struct's name starts with
131  * an upper-case S and its instance name with a lower-case s followed by its index
132  * number.
133  *//*--------------------------------------------------------------------*/
generateSharedMemoryObject(de::Random & rnd)134 void RandomSharedLayoutCase::generateSharedMemoryObject(de::Random &rnd)
135 {
136     const string name         = "S" + de::toString(m_interface.getNumSharedObjects() + 1);
137     const string instanceName = "s" + de::toString(m_interface.getNumSharedObjects() + 1);
138     SharedStruct &object      = m_interface.allocSharedObject(name, instanceName);
139     const int numVars         = rnd.getInt(2, m_maxSharedObjectMembers);
140 
141     for (int i = 0; i < numVars; i++)
142         generateSharedMemoryVar(rnd, object);
143 }
144 
generateSharedMemoryVar(de::Random & rnd,SharedStruct & object)145 void RandomSharedLayoutCase::generateSharedMemoryVar(de::Random &rnd, SharedStruct &object)
146 {
147     SharedStructVar var;
148     var.name = genName('a', 'z', object.getNumMembers() + 1);
149 
150     if ((m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0 || (m_features & FEATURE_STRUCTS) != 0)
151         var.type = generateType(rnd, 3, true);
152     else
153         var.type = generateType(rnd, 1, true);
154 
155     var.topLevelArraySize = 1;
156     if (var.type.isArrayType())
157         var.topLevelArraySize = var.type.getArraySize() == glu::VarType::UNSIZED_ARRAY ? 0 : var.type.getArraySize();
158 
159     object.addMember(var);
160 }
161 
generateType(de::Random & rnd,int typeDepth,bool arrayOk)162 glu::VarType RandomSharedLayoutCase::generateType(de::Random &rnd, int typeDepth, bool arrayOk)
163 {
164     const float structWeight = 0.7f;
165     const float arrayWeight  = 0.8f;
166 
167     if (typeDepth > 0 && rnd.getFloat() < structWeight && (m_features & FEATURE_STRUCTS))
168     {
169         vector<glu::VarType> memberTypes;
170         const int numMembers = rnd.getInt(1, m_maxStructMembers);
171 
172         // Generate members first so nested struct declarations are in correct order.
173         for (int i = 0; i < numMembers; i++)
174             memberTypes.push_back(generateType(rnd, typeDepth - 1, true));
175 
176         const string name                         = "s" + genName('A', 'Z', m_interface.getNumStructs() + 1);
177         de::SharedPtr<glu::StructType> structType = m_interface.allocStruct(name);
178 
179         DE_ASSERT(numMembers <= 'Z' - 'A');
180         for (int i = 0; i < numMembers; i++)
181             structType.get()->addMember((string("m") + static_cast<char>(('A' + i))).c_str(), memberTypes[i]);
182 
183         return glu::VarType(structType.get());
184     }
185     else if (typeDepth > 0 && m_maxArrayLength > 0 && arrayOk && rnd.getFloat() < arrayWeight)
186     {
187         const int arrayLength          = rnd.getInt(1, m_maxArrayLength);
188         const bool childArrayOk        = (m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0;
189         const glu::VarType elementType = generateType(rnd, typeDepth - 1, childArrayOk);
190 
191         return glu::VarType(elementType, arrayLength);
192     }
193     else
194     {
195         const float weight8Bit     = (m_features & FEATURE_8BIT_TYPES) ? 0.7f : 0.0f;
196         const float weight16Bit    = (m_features & FEATURE_16BIT_TYPES) ? 0.7f : 0.0f;
197         const float weightMatrices = (m_features & FEATURE_MATRICES) ? 0.3f : 0.0f;
198 
199         vector<glu::DataType> typeCandidates;
200         if (rnd.getFloat() < weight16Bit)
201         {
202             typeCandidates.push_back(glu::TYPE_UINT16);
203             typeCandidates.push_back(glu::TYPE_INT16);
204             typeCandidates.push_back(glu::TYPE_FLOAT16);
205 
206             if (m_features & FEATURE_VECTORS)
207             {
208                 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC2);
209                 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC3);
210                 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC4);
211                 typeCandidates.push_back(glu::TYPE_INT16_VEC2);
212                 typeCandidates.push_back(glu::TYPE_INT16_VEC3);
213                 typeCandidates.push_back(glu::TYPE_INT16_VEC4);
214                 typeCandidates.push_back(glu::TYPE_UINT16_VEC2);
215                 typeCandidates.push_back(glu::TYPE_UINT16_VEC3);
216                 typeCandidates.push_back(glu::TYPE_UINT16_VEC4);
217             }
218         }
219         else if (rnd.getFloat() < weight8Bit)
220         {
221             typeCandidates.push_back(glu::TYPE_UINT8);
222             typeCandidates.push_back(glu::TYPE_INT8);
223 
224             if (m_features & FEATURE_VECTORS)
225             {
226                 typeCandidates.push_back(glu::TYPE_INT8_VEC2);
227                 typeCandidates.push_back(glu::TYPE_INT8_VEC3);
228                 typeCandidates.push_back(glu::TYPE_INT8_VEC4);
229                 typeCandidates.push_back(glu::TYPE_UINT8_VEC2);
230                 typeCandidates.push_back(glu::TYPE_UINT8_VEC3);
231                 typeCandidates.push_back(glu::TYPE_UINT8_VEC4);
232             }
233         }
234         else
235         {
236             typeCandidates.push_back(glu::TYPE_FLOAT);
237             typeCandidates.push_back(glu::TYPE_INT);
238             typeCandidates.push_back(glu::TYPE_UINT);
239             typeCandidates.push_back(glu::TYPE_BOOL);
240 
241             if (m_features & FEATURE_VECTORS)
242             {
243                 typeCandidates.push_back(glu::TYPE_FLOAT_VEC2);
244                 typeCandidates.push_back(glu::TYPE_FLOAT_VEC3);
245                 typeCandidates.push_back(glu::TYPE_FLOAT_VEC4);
246                 typeCandidates.push_back(glu::TYPE_INT_VEC2);
247                 typeCandidates.push_back(glu::TYPE_INT_VEC3);
248                 typeCandidates.push_back(glu::TYPE_INT_VEC4);
249                 typeCandidates.push_back(glu::TYPE_UINT_VEC2);
250                 typeCandidates.push_back(glu::TYPE_UINT_VEC3);
251                 typeCandidates.push_back(glu::TYPE_UINT_VEC4);
252                 typeCandidates.push_back(glu::TYPE_BOOL_VEC2);
253                 typeCandidates.push_back(glu::TYPE_BOOL_VEC3);
254                 typeCandidates.push_back(glu::TYPE_BOOL_VEC4);
255             }
256         }
257 
258         if (rnd.getFloat() < weightMatrices)
259         {
260             typeCandidates.push_back(glu::TYPE_FLOAT_MAT2);
261             typeCandidates.push_back(glu::TYPE_FLOAT_MAT2X3);
262             typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X2);
263             typeCandidates.push_back(glu::TYPE_FLOAT_MAT3);
264             typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X4);
265             typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X2);
266             typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X3);
267             typeCandidates.push_back(glu::TYPE_FLOAT_MAT4);
268         }
269 
270         glu::DataType type = rnd.choose<glu::DataType>(typeCandidates.begin(), typeCandidates.end());
271         glu::Precision precision;
272 
273         if (glu::dataTypeSupportsPrecisionModifier(type))
274         {
275             const glu::Precision precisionCandidates[] = {glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP,
276                                                           glu::PRECISION_HIGHP};
277             precision                                  = rnd.choose<glu::Precision>(&precisionCandidates[0],
278                                                    &precisionCandidates[DE_LENGTH_OF_ARRAY(precisionCandidates)]);
279         }
280         else
281             precision = glu::PRECISION_LAST;
282 
283         return glu::VarType(type, precision);
284     }
285 }
286 
createSharedMemoryLayoutTests(tcu::TestContext & testCtx)287 tcu::TestCaseGroup *createSharedMemoryLayoutTests(tcu::TestContext &testCtx)
288 {
289     de::MovePtr<tcu::TestCaseGroup> sharedMemoryLayoutGroup(new tcu::TestCaseGroup(testCtx, "shared"));
290     tcu::TestCaseGroup *parentGroup = sharedMemoryLayoutGroup.get();
291     {
292         const uint32_t allBasicTypes = FEATURE_VECTORS | FEATURE_MATRICES;
293         const uint32_t unused        = FEATURE_UNUSED_MEMBERS | FEATURE_UNUSED_VARS;
294 
295         for (int i = 0; i < 3; ++i)
296         {
297             if (i == 1)
298             {
299                 parentGroup = new tcu::TestCaseGroup(testCtx, "16bit");
300                 sharedMemoryLayoutGroup->addChild(parentGroup);
301             }
302             else if (i == 2)
303             {
304                 parentGroup = new tcu::TestCaseGroup(testCtx, "8bit");
305                 sharedMemoryLayoutGroup->addChild(parentGroup);
306             }
307             const uint32_t use16BitTypes = i == 1 ? FEATURE_16BIT_TYPES : 0;
308             const uint32_t use8BitTypes  = i == 2 ? FEATURE_8BIT_TYPES : 0;
309 
310             createRandomCaseGroup(parentGroup, testCtx, "scalar_types", use8BitTypes | use16BitTypes | unused, 10, 0);
311             createRandomCaseGroup(parentGroup, testCtx, "vector_types",
312                                   use8BitTypes | use16BitTypes | unused | FEATURE_VECTORS, 10, 25);
313             createRandomCaseGroup(parentGroup, testCtx, "basic_types",
314                                   use8BitTypes | use16BitTypes | unused | allBasicTypes, 10, 50);
315             createRandomCaseGroup(parentGroup, testCtx, "basic_arrays",
316                                   use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_ARRAYS, 10, 50);
317             createRandomCaseGroup(parentGroup, testCtx, "arrays_of_arrays",
318                                   use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_ARRAYS |
319                                       FEATURE_ARRAYS_OF_ARRAYS,
320                                   10, 950);
321             createRandomCaseGroup(parentGroup, testCtx, "nested_structs",
322                                   use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_STRUCTS, 10, 100);
323             createRandomCaseGroup(parentGroup, testCtx, "nested_structs_arrays",
324                                   use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_STRUCTS |
325                                       FEATURE_ARRAYS | FEATURE_ARRAYS_OF_ARRAYS,
326                                   10, 150);
327         }
328     }
329 
330     return sharedMemoryLayoutGroup.release();
331 }
332 
333 } // namespace MemoryModel
334 } // namespace vkt
335