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, &params));
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