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 layout test case.
23 *//*--------------------------------------------------------------------*/
24
25 #include <vkDefs.hpp>
26 #include "deRandom.hpp"
27 #include "gluContextInfo.hpp"
28 #include "gluVarTypeUtil.hpp"
29 #include "tcuTestLog.hpp"
30
31 #include "vkBuilderUtil.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkRefUtil.hpp"
35 #include "vkRef.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkCmdUtil.hpp"
38
39 #include "vktMemoryModelSharedLayoutCase.hpp"
40 #include "util/vktTypeComparisonUtil.hpp"
41
42 namespace vkt
43 {
44 namespace MemoryModel
45 {
46
47 using glu::StructMember;
48 using glu::VarType;
49 using std::string;
50 using std::vector;
51 using tcu::TestLog;
52
53 namespace
54 {
computeReferenceLayout(const VarType & type,vector<SharedStructVarEntry> & entries)55 void computeReferenceLayout(const VarType &type, vector<SharedStructVarEntry> &entries)
56 {
57 if (type.isBasicType())
58 entries.push_back(SharedStructVarEntry(type.getBasicType(), 1));
59 else if (type.isArrayType())
60 {
61 const VarType &elemType = type.getElementType();
62
63 // Array of scalars, vectors or matrices.
64 if (elemType.isBasicType())
65 entries.push_back(SharedStructVarEntry(elemType.getBasicType(), type.getArraySize()));
66 else
67 {
68 DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
69 for (int i = 0; i < type.getArraySize(); i++)
70 computeReferenceLayout(type.getElementType(), entries);
71 }
72 }
73 else
74 {
75 DE_ASSERT(type.isStructType());
76 for (const auto &member : *type.getStructPtr())
77 computeReferenceLayout(member.getType(), entries);
78 }
79 }
80
computeReferenceLayout(SharedStructVar & var)81 void computeReferenceLayout(SharedStructVar &var)
82 {
83 // Top-level arrays need special care.
84 if (var.type.isArrayType())
85 computeReferenceLayout(var.type.getElementType(), var.entries);
86 else
87 computeReferenceLayout(var.type, var.entries);
88 }
89
generateValue(const SharedStructVarEntry & entry,de::Random & rnd,vector<string> & values)90 void generateValue(const SharedStructVarEntry &entry, de::Random &rnd, vector<string> &values)
91 {
92 const glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
93 const int scalarSize = glu::getDataTypeScalarSize(entry.type);
94 const int arraySize = entry.arraySize;
95 const bool isMatrix = glu::isDataTypeMatrix(entry.type);
96 const int numVecs = isMatrix ? glu::getDataTypeMatrixNumColumns(entry.type) : 1;
97 const int vecSize = scalarSize / numVecs;
98
99 DE_ASSERT(scalarSize % numVecs == 0);
100 DE_ASSERT(arraySize >= 0);
101
102 string generatedValue;
103 for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
104 {
105 for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
106 {
107 for (int compNdx = 0; compNdx < vecSize; compNdx++)
108 {
109 switch (scalarType)
110 {
111 case glu::TYPE_INT:
112 case glu::TYPE_INT8:
113 case glu::TYPE_INT16:
114 // Fall through. This fits into all the types above.
115 generatedValue = de::toString(rnd.getInt(-9, 9));
116 break;
117 case glu::TYPE_UINT:
118 case glu::TYPE_UINT8:
119 case glu::TYPE_UINT16:
120 // Fall through. This fits into all the types above.
121 generatedValue = de::toString(rnd.getInt(0, 9)).append("u");
122 break;
123 case glu::TYPE_FLOAT:
124 case glu::TYPE_FLOAT16:
125 // Fall through. This fits into all the types above.
126 generatedValue = de::floatToString(static_cast<float>(rnd.getInt(-9, 9)), 1);
127 break;
128 case glu::TYPE_BOOL:
129 generatedValue = rnd.getBool() ? "true" : "false";
130 break;
131 default:
132 DE_ASSERT(false);
133 }
134
135 values.push_back(generatedValue);
136 }
137 }
138 }
139 }
140
getStructMemberName(const SharedStructVar & var,const glu::TypeComponentVector & accessPath)141 string getStructMemberName(const SharedStructVar &var, const glu::TypeComponentVector &accessPath)
142 {
143 std::ostringstream name;
144
145 name << "." << var.name;
146
147 for (auto pathComp = accessPath.begin(); pathComp != accessPath.end(); pathComp++)
148 {
149 if (pathComp->type == glu::VarTypeComponent::STRUCT_MEMBER)
150 {
151 const VarType curType = glu::getVarType(var.type, accessPath.begin(), pathComp);
152 const glu::StructType *structPtr = curType.getStructPtr();
153
154 name << "." << structPtr->getMember(pathComp->index).getName();
155 }
156 else if (pathComp->type == glu::VarTypeComponent::ARRAY_ELEMENT)
157 name << "[" << pathComp->index << "]";
158 else
159 DE_ASSERT(false);
160 }
161
162 return name.str();
163 }
164 } // namespace
165
allocStruct(const string & name)166 NamedStructSP ShaderInterface::allocStruct(const string &name)
167 {
168 m_structs.emplace_back(new glu::StructType(name.c_str()));
169 return m_structs.back();
170 }
171
allocSharedObject(const string & name,const string & instanceName)172 SharedStruct &ShaderInterface::allocSharedObject(const string &name, const string &instanceName)
173 {
174 m_sharedMemoryObjects.emplace_back(name, instanceName);
175 return m_sharedMemoryObjects.back();
176 }
177
generateCompareFuncs(std::ostream & str,const ShaderInterface & interface)178 void generateCompareFuncs(std::ostream &str, const ShaderInterface &interface)
179 {
180 std::set<glu::DataType> types;
181 std::set<glu::DataType> compareFuncs;
182
183 // Collect unique basic types.
184 for (const auto &sharedObj : interface.getSharedObjects())
185 for (const auto &var : sharedObj)
186 vkt::typecomputil::collectUniqueBasicTypes(types, var.type);
187
188 // Set of compare functions required.
189 for (const auto &type : types)
190 vkt::typecomputil::getCompareDependencies(compareFuncs, type);
191
192 for (int type = 0; type < glu::TYPE_LAST; ++type)
193 if (compareFuncs.find(glu::DataType(type)) != compareFuncs.end())
194 str << vkt::typecomputil::getCompareFuncForType(glu::DataType(type));
195 }
196
generateSharedMemoryWrites(std::ostream & src,const SharedStruct & object,const SharedStructVar & var,const glu::SubTypeAccess & accessPath,vector<string>::const_iterator & valueIter,bool compare)197 void generateSharedMemoryWrites(std::ostream &src, const SharedStruct &object, const SharedStructVar &var,
198 const glu::SubTypeAccess &accessPath, vector<string>::const_iterator &valueIter,
199 bool compare)
200 {
201 const VarType curType = accessPath.getType();
202
203 if (curType.isArrayType())
204 {
205 const int arraySize = curType.getArraySize();
206 for (int i = 0; i < arraySize; i++)
207 generateSharedMemoryWrites(src, object, var, accessPath.element(i), valueIter, compare);
208 }
209 else if (curType.isStructType())
210 {
211 const int numMembers = curType.getStructPtr()->getNumMembers();
212 for (int i = 0; i < numMembers; i++)
213 generateSharedMemoryWrites(src, object, var, accessPath.member(i), valueIter, compare);
214 }
215 else
216 {
217 DE_ASSERT(curType.isBasicType());
218
219 const glu::DataType basicType = curType.getBasicType();
220 const string typeName = glu::getDataTypeName(basicType);
221 const string sharedObjectVarName = object.getInstanceName();
222 const string structMember = getStructMemberName(var, accessPath.getPath());
223 const glu::DataType promoteType = vkt::typecomputil::getPromoteType(basicType);
224
225 int numElements = glu::getDataTypeScalarSize(basicType);
226 if (glu::isDataTypeMatrix(basicType))
227 numElements = glu::getDataTypeMatrixNumColumns(basicType) * glu::getDataTypeMatrixNumRows(basicType);
228
229 if (compare)
230 {
231 src << "\t"
232 << "allOk = compare_" << typeName << "(";
233 // Comparison functions use 32-bit values. Convert 8/16-bit scalar and vector types if necessary.
234 // E.g. uint8_t becomes int.
235 if (basicType != promoteType || numElements > 1)
236 src << glu::getDataTypeName(promoteType) << "(";
237 }
238 else
239 {
240 src << "\t" << sharedObjectVarName << structMember << " = "
241 << "";
242 // If multiple literals or a 8/16-bit literal is assigned, the variable must be
243 // initialized with the constructor.
244 if (basicType != promoteType || numElements > 1)
245 src << glu::getDataTypeName(basicType) << "(";
246 }
247
248 for (int i = 0; i < numElements; i++)
249 src << (i != 0 ? ", " : "") << *valueIter++;
250
251 if (basicType != promoteType)
252 src << ")";
253 else if (numElements > 1)
254 src << ")";
255
256 // Write the variable in the shared memory as the next argument for the comparison function.
257 // Initialize it as a new 32-bit variable in the case it's a 8-bit or a 16-bit variable.
258 if (compare)
259 {
260 if (basicType != promoteType)
261 src << ", " << glu::getDataTypeName(promoteType) << "(" << sharedObjectVarName << structMember
262 << ")) && allOk";
263 else
264 src << ", " << sharedObjectVarName << structMember << ") && allOk";
265 }
266
267 src << ";\n";
268 }
269 }
270
generateComputeShader(ShaderInterface & interface)271 string generateComputeShader(ShaderInterface &interface)
272 {
273 std::ostringstream src;
274
275 src << "#version 450\n";
276
277 if (interface.is16BitTypesEnabled())
278 src << "#extension GL_EXT_shader_explicit_arithmetic_types : enable\n";
279 if (interface.is8BitTypesEnabled())
280 src << "#extension GL_EXT_shader_explicit_arithmetic_types_int8 : enable\n";
281
282 src << "layout(local_size_x = 1) in;\n";
283 src << "\n";
284
285 src << "layout(std140, binding = 0) buffer block { highp uint passed; };\n";
286
287 // Output definitions for the struct fields of the shared memory objects.
288 std::vector<NamedStructSP> &namedStructs = interface.getStructs();
289
290 for (const auto &s : namedStructs)
291 src << glu::declare(s.get()) << ";\n";
292
293 // Output definitions for the shared memory structs.
294 for (auto &sharedObj : interface.getSharedObjects())
295 {
296 src << "struct " << sharedObj.getName() << " {\n";
297
298 for (auto &var : sharedObj)
299 src << "\t" << glu::declare(var.type, var.name, 1) << ";\n";
300
301 src << "};\n";
302 }
303
304 // Comparison utilities.
305 src << "\n";
306 generateCompareFuncs(src, interface);
307
308 src << "\n";
309 for (auto &sharedObj : interface.getSharedObjects())
310 src << "shared " << sharedObj.getName() << " " << sharedObj.getInstanceName() << ";\n";
311
312 src << "\n";
313 src << "void main (void) {\n";
314
315 for (auto &sharedObj : interface.getSharedObjects())
316 {
317 for (const auto &var : sharedObj)
318 {
319 vector<string>::const_iterator valueIter = var.entryValues.begin();
320 generateSharedMemoryWrites(src, sharedObj, var, glu::SubTypeAccess(var.type), valueIter, false);
321 }
322 }
323
324 src << "\n";
325 src << "\tbarrier();\n";
326 src << "\tmemoryBarrier();\n";
327 src << "\tbool allOk = true;\n";
328
329 for (auto &sharedObj : interface.getSharedObjects())
330 {
331 for (const auto &var : sharedObj)
332 {
333 vector<string>::const_iterator valueIter = var.entryValues.begin();
334 generateSharedMemoryWrites(src, sharedObj, var, glu::SubTypeAccess(var.type), valueIter, true);
335 }
336 }
337
338 src << "\tif (allOk)\n"
339 << "\t\tpassed++;\n"
340 << "\n";
341
342 src << "}\n";
343
344 return src.str();
345 }
346
checkSupport(Context & context) const347 void SharedLayoutCase::checkSupport(Context &context) const
348 {
349 if ((m_interface.is16BitTypesEnabled() || m_interface.is8BitTypesEnabled()) &&
350 !context.isDeviceFunctionalitySupported("VK_KHR_shader_float16_int8"))
351 TCU_THROW(NotSupportedError, "VK_KHR_shader_float16_int8 extension for 16-/8-bit types not supported");
352
353 const vk::VkPhysicalDeviceVulkan12Features features = context.getDeviceVulkan12Features();
354 if (m_interface.is16BitTypesEnabled() && !features.shaderFloat16)
355 TCU_THROW(NotSupportedError, "16-bit types not supported");
356 if (m_interface.is8BitTypesEnabled() && !features.shaderInt8)
357 TCU_THROW(NotSupportedError, "8-bit types not supported");
358 }
359
iterate(void)360 tcu::TestStatus SharedLayoutCaseInstance::iterate(void)
361 {
362 const vk::DeviceInterface &vk = m_context.getDeviceInterface();
363 const vk::VkDevice device = m_context.getDevice();
364 const vk::VkQueue queue = m_context.getUniversalQueue();
365 const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
366 vk::Allocator &allocator = m_context.getDefaultAllocator();
367 const uint32_t bufferSize = 4;
368
369 // Create descriptor set
370 const vk::VkBufferCreateInfo params = {
371 vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
372 DE_NULL, // pNext
373 0u, // flags
374 bufferSize, // size
375 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // usage
376 vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
377 1u, // queueFamilyCount
378 &queueFamilyIndex // pQueueFamilyIndices
379 };
380
381 vk::Move<vk::VkBuffer> buffer(vk::createBuffer(vk, device, ¶ms));
382 vk::VkMemoryRequirements requirements = getBufferMemoryRequirements(vk, device, *buffer);
383 de::MovePtr<vk::Allocation> bufferAlloc(allocator.allocate(requirements, vk::MemoryRequirement::HostVisible));
384 VK_CHECK(vk.bindBufferMemory(device, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset()));
385
386 deMemset(bufferAlloc->getHostPtr(), 0, bufferSize);
387 flushMappedMemoryRange(vk, device, bufferAlloc->getMemory(), bufferAlloc->getOffset(), requirements.size);
388
389 vk::DescriptorSetLayoutBuilder setLayoutBuilder;
390 vk::DescriptorPoolBuilder poolBuilder;
391
392 setLayoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
393
394 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, uint32_t(1));
395
396 const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(setLayoutBuilder.build(vk, device));
397 const vk::Unique<vk::VkDescriptorPool> descriptorPool(
398 poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
399
400 const vk::VkDescriptorSetAllocateInfo allocInfo = {
401 vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
402 DE_NULL, // const void* pNext;
403 *descriptorPool, // VkDescriptorPool descriptorPool;
404 1u, // uint32_t descriptorSetCount;
405 &descriptorSetLayout.get(), // const VkDescriptorSetLayout *pSetLayouts;
406 };
407
408 const vk::Unique<vk::VkDescriptorSet> descriptorSet(allocateDescriptorSet(vk, device, &allocInfo));
409 const vk::VkDescriptorBufferInfo descriptorInfo = makeDescriptorBufferInfo(*buffer, 0ull, bufferSize);
410
411 vk::DescriptorSetUpdateBuilder setUpdateBuilder;
412 std::vector<vk::VkDescriptorBufferInfo> descriptors;
413
414 setUpdateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
415 vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo);
416
417 setUpdateBuilder.update(vk, device);
418
419 const vk::VkPipelineLayoutCreateInfo pipelineLayoutParams = {
420 vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
421 DE_NULL, // const void* pNext;
422 (vk::VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
423 1u, // uint32_t descriptorSetCount;
424 &*descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts;
425 0u, // uint32_t pushConstantRangeCount;
426 DE_NULL // const VkPushConstantRange* pPushConstantRanges;
427 };
428 vk::Move<vk::VkPipelineLayout> pipelineLayout(createPipelineLayout(vk, device, &pipelineLayoutParams));
429
430 vk::Move<vk::VkShaderModule> shaderModule(
431 createShaderModule(vk, device, m_context.getBinaryCollection().get("compute"), 0));
432 const vk::VkPipelineShaderStageCreateInfo pipelineShaderStageParams = {
433 vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
434 DE_NULL, // const void* pNext;
435 (vk::VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
436 vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStage stage;
437 *shaderModule, // VkShaderModule module;
438 "main", // const char* pName;
439 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
440 };
441 const vk::VkComputePipelineCreateInfo pipelineCreateInfo = {
442 vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
443 DE_NULL, // const void* pNext;
444 0, // VkPipelineCreateFlags flags;
445 pipelineShaderStageParams, // VkPipelineShaderStageCreateInfo stage;
446 *pipelineLayout, // VkPipelineLayout layout;
447 DE_NULL, // VkPipeline basePipelineHandle;
448 0, // int32_t basePipelineIndex;
449 };
450
451 vk::Move<vk::VkPipeline> pipeline(createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo));
452 vk::Move<vk::VkCommandPool> cmdPool(
453 createCommandPool(vk, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
454 vk::Move<vk::VkCommandBuffer> cmdBuffer(
455 allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
456
457 beginCommandBuffer(vk, *cmdBuffer, 0u);
458
459 vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
460
461 vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
462 &descriptorSet.get(), 0u, DE_NULL);
463
464 vk.cmdDispatch(*cmdBuffer, 1, 1, 1);
465
466 endCommandBuffer(vk, *cmdBuffer);
467
468 submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
469
470 // Read back passed data
471 bool counterOk;
472 const int refCount = 1;
473 int resCount = 0;
474
475 invalidateAlloc(vk, device, *bufferAlloc);
476
477 resCount = *(static_cast<const int *>(bufferAlloc->getHostPtr()));
478
479 counterOk = (refCount == resCount);
480 if (!counterOk)
481 m_context.getTestContext().getLog()
482 << TestLog::Message << "Error: passed = " << resCount << ", expected " << refCount << TestLog::EndMessage;
483
484 // Validate result
485 if (counterOk)
486 return tcu::TestStatus::pass("Counter value OK");
487
488 return tcu::TestStatus::fail("Counter value incorrect");
489 }
490
initPrograms(vk::SourceCollections & programCollection) const491 void SharedLayoutCase::initPrograms(vk::SourceCollections &programCollection) const
492 {
493 DE_ASSERT(!m_computeShaderSrc.empty());
494 programCollection.glslSources.add("compute") << glu::ComputeSource(m_computeShaderSrc);
495 }
496
createInstance(Context & context) const497 TestInstance *SharedLayoutCase::createInstance(Context &context) const
498 {
499 return new SharedLayoutCaseInstance(context);
500 }
501
delayedInit(void)502 void SharedLayoutCase::delayedInit(void)
503 {
504
505 for (auto &sharedObj : m_interface.getSharedObjects())
506 for (auto &var : sharedObj)
507 computeReferenceLayout(var);
508
509 uint32_t seed = deStringHash(getName()) ^ 0xad2f7214;
510 de::Random rnd(seed);
511
512 for (auto &sharedObj : m_interface.getSharedObjects())
513 for (auto &var : sharedObj)
514 for (int i = 0; i < var.topLevelArraySize; i++)
515 for (auto &entry : var.entries)
516 generateValue(entry, rnd, var.entryValues);
517
518 m_computeShaderSrc = generateComputeShader(m_interface);
519 }
520
521 } // namespace MemoryModel
522 } // namespace vkt
523