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 char * description,const deUint32 features,const int numCases,deUint32 baseSeed)94 void createRandomCaseGroup (tcu::TestCaseGroup* parentGroup, tcu::TestContext &testCtx, const char *groupName,
95 const char *description, const deUint32 features, const int numCases, deUint32 baseSeed)
96 {
97 tcu::TestCaseGroup *group = new tcu::TestCaseGroup(testCtx, groupName, description);
98 parentGroup->addChild(group);
99
100 baseSeed += static_cast<deUint32>(testCtx.getCommandLine().getBaseSeed());
101
102 for (int i = 0; i < numCases; i++)
103 group->addChild(new RandomSharedLayoutCase(testCtx, de::toString(i).c_str(), "", features, static_cast<deUint32>(i + baseSeed)));
104 }
105 } // anonymous
106
RandomSharedLayoutCase(tcu::TestContext & testCtx,const char * name,const char * description,deUint32 features,deUint32 seed)107 RandomSharedLayoutCase::RandomSharedLayoutCase (tcu::TestContext &testCtx, const char *name, const char *description,
108 deUint32 features, deUint32 seed)
109 : SharedLayoutCase(testCtx, name, description)
110 , m_features(features)
111 , m_maxArrayLength((features & FEATURE_ARRAYS) ? 3 : 0)
112 , m_seed(seed)
113 {
114 de::Random rnd(m_seed);
115
116 m_interface.enable16BitTypes(features & FEATURE_16BIT_TYPES);
117 m_interface.enable8BitTypes(features & FEATURE_8BIT_TYPES);
118
119 for (int i = 0; i < rnd.getInt(1, m_maxSharedObjects); i++)
120 generateSharedMemoryObject(rnd);
121
122 init();
123 }
124
125 /*--------------------------------------------------------------------*//*!
126 * \brief Creates definitions for shared memory structs.
127 * \param rnd Random value generator used for deciding the type of the variable.
128 *
129 * Creates definitions for shared memory structs. Each struct's name starts with
130 * an upper-case S and its instance name with a lower-case s followed by its index
131 * number.
132 *//*--------------------------------------------------------------------*/
generateSharedMemoryObject(de::Random & rnd)133 void RandomSharedLayoutCase::generateSharedMemoryObject (de::Random &rnd)
134 {
135 const string name = "S" + de::toString(m_interface.getNumSharedObjects() + 1);
136 const string instanceName = "s" + de::toString(m_interface.getNumSharedObjects() + 1);
137 SharedStruct &object = m_interface.allocSharedObject(name, instanceName);
138 const int numVars = rnd.getInt(2, m_maxSharedObjectMembers);
139
140 for (int i = 0; i < numVars; i++)
141 generateSharedMemoryVar(rnd, object);
142 }
143
generateSharedMemoryVar(de::Random & rnd,SharedStruct & object)144 void RandomSharedLayoutCase::generateSharedMemoryVar (de::Random &rnd, SharedStruct &object)
145 {
146 SharedStructVar var;
147 var.name = genName('a', 'z', object.getNumMembers() + 1);
148
149 if ((m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0 || (m_features & FEATURE_STRUCTS) != 0)
150 var.type = generateType(rnd, 3, true);
151 else
152 var.type = generateType(rnd, 1, true);
153
154 var.topLevelArraySize = 1;
155 if (var.type.isArrayType())
156 var.topLevelArraySize = var.type.getArraySize() == glu::VarType::UNSIZED_ARRAY ? 0 : var.type.getArraySize();
157
158 object.addMember(var);
159 }
160
generateType(de::Random & rnd,int typeDepth,bool arrayOk)161 glu::VarType RandomSharedLayoutCase::generateType (de::Random &rnd, int typeDepth, bool arrayOk)
162 {
163 const float structWeight = 0.7f;
164 const float arrayWeight = 0.8f;
165
166 if (typeDepth > 0 && rnd.getFloat() < structWeight && (m_features & FEATURE_STRUCTS))
167 {
168 vector<glu::VarType> memberTypes;
169 const int numMembers = rnd.getInt(1, m_maxStructMembers);
170
171 // Generate members first so nested struct declarations are in correct order.
172 for (int i = 0; i < numMembers; i++)
173 memberTypes.push_back(generateType(rnd, typeDepth - 1, true));
174
175 const string name = "s" + genName('A', 'Z', m_interface.getNumStructs() + 1);
176 de::SharedPtr<glu::StructType> structType = m_interface.allocStruct(name);
177
178 DE_ASSERT(numMembers <= 'Z' - 'A');
179 for (int i = 0; i < numMembers; i++)
180 structType.get()->addMember((string("m") + static_cast<char>(('A' + i))).c_str(), memberTypes[i]);
181
182 return glu::VarType(structType.get());
183 }
184 else if (typeDepth > 0 && m_maxArrayLength > 0 && arrayOk && rnd.getFloat() < arrayWeight)
185 {
186 const int arrayLength = rnd.getInt(1, m_maxArrayLength);
187 const bool childArrayOk = (m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0;
188 const glu::VarType elementType = generateType(rnd, typeDepth - 1, childArrayOk);
189
190 return glu::VarType(elementType, arrayLength);
191 }
192 else
193 {
194 const float weight8Bit = (m_features & FEATURE_8BIT_TYPES) ? 0.7f : 0.0f;
195 const float weight16Bit = (m_features & FEATURE_16BIT_TYPES) ? 0.7f : 0.0f;
196 const float weightMatrices = (m_features & FEATURE_MATRICES) ? 0.3f : 0.0f;
197
198 vector<glu::DataType> typeCandidates;
199 if (rnd.getFloat() < weight16Bit)
200 {
201 typeCandidates.push_back(glu::TYPE_UINT16);
202 typeCandidates.push_back(glu::TYPE_INT16);
203 typeCandidates.push_back(glu::TYPE_FLOAT16);
204
205 if (m_features & FEATURE_VECTORS)
206 {
207 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC2);
208 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC3);
209 typeCandidates.push_back(glu::TYPE_FLOAT16_VEC4);
210 typeCandidates.push_back(glu::TYPE_INT16_VEC2);
211 typeCandidates.push_back(glu::TYPE_INT16_VEC3);
212 typeCandidates.push_back(glu::TYPE_INT16_VEC4);
213 typeCandidates.push_back(glu::TYPE_UINT16_VEC2);
214 typeCandidates.push_back(glu::TYPE_UINT16_VEC3);
215 typeCandidates.push_back(glu::TYPE_UINT16_VEC4);
216 }
217 }
218 else if (rnd.getFloat() < weight8Bit)
219 {
220 typeCandidates.push_back(glu::TYPE_UINT8);
221 typeCandidates.push_back(glu::TYPE_INT8);
222
223 if (m_features & FEATURE_VECTORS)
224 {
225 typeCandidates.push_back(glu::TYPE_INT8_VEC2);
226 typeCandidates.push_back(glu::TYPE_INT8_VEC3);
227 typeCandidates.push_back(glu::TYPE_INT8_VEC4);
228 typeCandidates.push_back(glu::TYPE_UINT8_VEC2);
229 typeCandidates.push_back(glu::TYPE_UINT8_VEC3);
230 typeCandidates.push_back(glu::TYPE_UINT8_VEC4);
231 }
232 }
233 else
234 {
235 typeCandidates.push_back(glu::TYPE_FLOAT);
236 typeCandidates.push_back(glu::TYPE_INT);
237 typeCandidates.push_back(glu::TYPE_UINT);
238 typeCandidates.push_back(glu::TYPE_BOOL);
239
240 if (m_features & FEATURE_VECTORS)
241 {
242 typeCandidates.push_back(glu::TYPE_FLOAT_VEC2);
243 typeCandidates.push_back(glu::TYPE_FLOAT_VEC3);
244 typeCandidates.push_back(glu::TYPE_FLOAT_VEC4);
245 typeCandidates.push_back(glu::TYPE_INT_VEC2);
246 typeCandidates.push_back(glu::TYPE_INT_VEC3);
247 typeCandidates.push_back(glu::TYPE_INT_VEC4);
248 typeCandidates.push_back(glu::TYPE_UINT_VEC2);
249 typeCandidates.push_back(glu::TYPE_UINT_VEC3);
250 typeCandidates.push_back(glu::TYPE_UINT_VEC4);
251 typeCandidates.push_back(glu::TYPE_BOOL_VEC2);
252 typeCandidates.push_back(glu::TYPE_BOOL_VEC3);
253 typeCandidates.push_back(glu::TYPE_BOOL_VEC4);
254 }
255 }
256
257 if (rnd.getFloat() < weightMatrices)
258 {
259 typeCandidates.push_back(glu::TYPE_FLOAT_MAT2);
260 typeCandidates.push_back(glu::TYPE_FLOAT_MAT2X3);
261 typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X2);
262 typeCandidates.push_back(glu::TYPE_FLOAT_MAT3);
263 typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X4);
264 typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X2);
265 typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X3);
266 typeCandidates.push_back(glu::TYPE_FLOAT_MAT4);
267 }
268
269 glu::DataType type = rnd.choose<glu::DataType>(typeCandidates.begin(), typeCandidates.end());
270 glu::Precision precision;
271
272 if (glu::dataTypeSupportsPrecisionModifier(type))
273 {
274 const glu::Precision precisionCandidates[] = { glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP};
275 precision = rnd.choose<glu::Precision>(&precisionCandidates[0],
276 &precisionCandidates[DE_LENGTH_OF_ARRAY(precisionCandidates)]);
277 }
278 else
279 precision = glu::PRECISION_LAST;
280
281 return glu::VarType(type, precision);
282 }
283 }
284
createSharedMemoryLayoutTests(tcu::TestContext & testCtx)285 tcu::TestCaseGroup* createSharedMemoryLayoutTests (tcu::TestContext &testCtx)
286 {
287 de::MovePtr<tcu::TestCaseGroup> sharedMemoryLayoutGroup(new tcu::TestCaseGroup(testCtx, "shared", "Shared memory layout tests"));
288 tcu::TestCaseGroup *parentGroup = sharedMemoryLayoutGroup.get();
289 {
290 const deUint32 allBasicTypes = FEATURE_VECTORS | FEATURE_MATRICES;
291 const deUint32 unused = FEATURE_UNUSED_MEMBERS | FEATURE_UNUSED_VARS;
292
293 for (int i = 0; i < 3; ++i)
294 {
295 if (i == 1)
296 {
297 parentGroup = new tcu::TestCaseGroup(testCtx, "16bit", "16bit");
298 sharedMemoryLayoutGroup->addChild(parentGroup);
299 }
300 else if (i == 2)
301 {
302 parentGroup = new tcu::TestCaseGroup(testCtx, "8bit", "8bit");
303 sharedMemoryLayoutGroup->addChild(parentGroup);
304 }
305 const deUint32 use16BitTypes = i == 1 ? FEATURE_16BIT_TYPES : 0;
306 const deUint32 use8BitTypes = i == 2 ? FEATURE_8BIT_TYPES : 0;
307
308 createRandomCaseGroup(parentGroup, testCtx, "scalar_types", "Scalar types only",
309 use8BitTypes | use16BitTypes | unused, 10, 0);
310 createRandomCaseGroup(parentGroup, testCtx, "vector_types", "Scalar and vector types only",
311 use8BitTypes | use16BitTypes | unused | FEATURE_VECTORS, 10, 25);
312 createRandomCaseGroup(parentGroup, testCtx, "basic_types", "All basic types",
313 use8BitTypes | use16BitTypes | unused | allBasicTypes, 10, 50);
314 createRandomCaseGroup(parentGroup, testCtx, "basic_arrays", "Arrays",
315 use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_ARRAYS, 10, 50);
316 createRandomCaseGroup(parentGroup, testCtx, "arrays_of_arrays", "Arrays of arrays",
317 use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_ARRAYS |
318 FEATURE_ARRAYS_OF_ARRAYS, 10, 950);
319 createRandomCaseGroup(parentGroup, testCtx, "nested_structs", "Nested structs",
320 use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_STRUCTS, 10, 100);
321 createRandomCaseGroup(parentGroup, testCtx, "nested_structs_arrays", "Nested structs, arrays",
322 use8BitTypes | use16BitTypes | unused | allBasicTypes | FEATURE_STRUCTS |
323 FEATURE_ARRAYS | FEATURE_ARRAYS_OF_ARRAYS, 10, 150);
324 }
325 }
326
327 return sharedMemoryLayoutGroup.release();
328 }
329
330 } // MemoryModel
331 } // vkt
332