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 tcu::TestLog;
48 using std::string;
49 using std::vector;
50 using glu::VarType;
51 using glu::StructMember;
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 } // anonymous
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,
198 const SharedStructVar &var, const glu::SubTypeAccess &accessPath,
199 vector<string>::const_iterator &valueIter, 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" << "allOk" << " = " << "allOk" << " && compare_" << typeName << "(";
232 // Comparison functions use 32-bit values. Convert 8/16-bit scalar and vector types if necessary.
233 // E.g. uint8_t becomes int.
234 if (basicType != promoteType || numElements > 1)
235 src << glu::getDataTypeName(promoteType) << "(";
236 }
237 else
238 {
239 src << "\t" << sharedObjectVarName << structMember << " = " << "";
240 // If multiple literals or a 8/16-bit literal is assigned, the variable must be
241 // initialized with the constructor.
242 if (basicType != promoteType || numElements > 1)
243 src << glu::getDataTypeName(basicType) << "(";
244 }
245
246 for (int i = 0; i < numElements; i++)
247 src << (i != 0 ? ", " : "") << *valueIter++;
248
249 if (basicType != promoteType)
250 src << ")";
251 else if (numElements > 1)
252 src << ")";
253
254 // Write the variable in the shared memory as the next argument for the comparison function.
255 // Initialize it as a new 32-bit variable in the case it's a 8-bit or a 16-bit variable.
256 if (compare)
257 {
258 if (basicType != promoteType)
259 src << ", " << glu::getDataTypeName(promoteType) << "(" << sharedObjectVarName
260 << structMember
261 << "))";
262 else
263 src << ", " << sharedObjectVarName << structMember << ")";
264 }
265
266 src << ";\n";
267 }
268 }
269
generateComputeShader(ShaderInterface & interface)270 string generateComputeShader (ShaderInterface &interface)
271 {
272 std::ostringstream src;
273
274 src << "#version 450\n";
275
276 if (interface.is16BitTypesEnabled())
277 src << "#extension GL_EXT_shader_explicit_arithmetic_types : enable\n";
278 if (interface.is8BitTypesEnabled())
279 src << "#extension GL_EXT_shader_explicit_arithmetic_types_int8 : enable\n";
280
281 src << "layout(local_size_x = 1) in;\n";
282 src << "\n";
283
284 src << "layout(std140, binding = 0) buffer block { highp uint passed; };\n";
285
286 // Output definitions for the struct fields of the shared memory objects.
287 std::vector<NamedStructSP>& namedStructs = interface.getStructs();
288
289 for (const auto& s: namedStructs)
290 src << glu::declare(s.get()) << ";\n";
291
292 // Output definitions for the shared memory structs.
293 for (auto& sharedObj : interface.getSharedObjects())
294 {
295 src << "struct " << sharedObj.getName() << " {\n";
296
297 for (auto& var : sharedObj)
298 src << "\t" << glu::declare(var.type, var.name, 1) << ";\n";
299
300 src << "};\n";
301 }
302
303 // Comparison utilities.
304 src << "\n";
305 generateCompareFuncs(src, interface);
306
307 src << "\n";
308 for (auto& sharedObj : interface.getSharedObjects())
309 src << "shared " << sharedObj.getName() << " " << sharedObj.getInstanceName() << ";\n";
310
311 src << "\n";
312 src << "void main (void) {\n";
313
314 for (auto& sharedObj : interface.getSharedObjects())
315 {
316 for (const auto& var : sharedObj)
317 {
318 vector<string>::const_iterator valueIter = var.entryValues.begin();
319 generateSharedMemoryWrites(src, sharedObj, var, glu::SubTypeAccess(var.type), valueIter, false);
320 }
321 }
322
323 src << "\n";
324 src << "\tbarrier();\n";
325 src << "\tmemoryBarrier();\n";
326 src << "\tbool allOk = true;\n";
327
328 for (auto& sharedObj : interface.getSharedObjects())
329 {
330 for (const auto& var : sharedObj)
331 {
332 vector<string>::const_iterator valueIter = var.entryValues.begin();
333 generateSharedMemoryWrites(src, sharedObj, var, glu::SubTypeAccess(var.type), valueIter, true);
334 }
335 }
336
337 src << "\tif (allOk)\n"
338 << "\t\tpassed++;\n"
339 << "\n";
340
341 src << "}\n";
342
343 return src.str();
344 }
345
checkSupport(Context & context) const346 void SharedLayoutCase::checkSupport(Context& context) const
347 {
348 if ((m_interface.is16BitTypesEnabled() || m_interface.is8BitTypesEnabled())
349 && !context.isDeviceFunctionalitySupported("VK_KHR_shader_float16_int8"))
350 TCU_THROW(NotSupportedError, "VK_KHR_shader_float16_int8 extension for 16-/8-bit types not supported");
351
352 const vk::VkPhysicalDeviceVulkan12Features features = context.getDeviceVulkan12Features();
353 if (m_interface.is16BitTypesEnabled() && !features.shaderFloat16)
354 TCU_THROW(NotSupportedError, "16-bit types not supported");
355 if (m_interface.is8BitTypesEnabled() && !features.shaderInt8)
356 TCU_THROW(NotSupportedError, "8-bit types not supported");
357 }
358
iterate(void)359 tcu::TestStatus SharedLayoutCaseInstance::iterate (void)
360 {
361 const vk::DeviceInterface &vk = m_context.getDeviceInterface();
362 const vk::VkDevice device = m_context.getDevice();
363 const vk::VkQueue queue = m_context.getUniversalQueue();
364 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
365 const deUint32 bufferSize = 4;
366
367 // Create descriptor set
368 const vk::VkBufferCreateInfo params =
369 {
370 vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
371 DE_NULL, // pNext
372 0u, // flags
373 bufferSize, // size
374 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, // usage
375 vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
376 1u, // queueFamilyCount
377 &queueFamilyIndex // pQueueFamilyIndices
378 };
379
380 vk::Move<vk::VkBuffer> buffer (vk::createBuffer(vk, device, ¶ms));
381
382 de::MovePtr<vk::Allocation> bufferAlloc (vk::bindBuffer (m_context.getDeviceInterface(), m_context.getDevice(),
383 m_context.getDefaultAllocator(), *buffer, vk::MemoryRequirement::HostVisible));
384
385 deMemset(bufferAlloc->getHostPtr(), 0, bufferSize);
386 flushMappedMemoryRange(vk, device, bufferAlloc->getMemory(), bufferAlloc->getOffset(), bufferSize);
387
388 vk::DescriptorSetLayoutBuilder setLayoutBuilder;
389 vk::DescriptorPoolBuilder poolBuilder;
390
391 setLayoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
392
393 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, deUint32(1));
394
395 const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout (setLayoutBuilder.build(vk, device));
396 const vk::Unique<vk::VkDescriptorPool> descriptorPool (poolBuilder.build(vk, device,
397 vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
398
399 const vk::VkDescriptorSetAllocateInfo allocInfo =
400 {
401 vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
402 DE_NULL, // const void* pNext;
403 *descriptorPool, // VkDescriptorPool descriptorPool;
404 1u, // deUint32 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 {
421 vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
422 DE_NULL, // const void* pNext;
423 (vk::VkPipelineLayoutCreateFlags) 0, // VkPipelineLayoutCreateFlags flags;
424 1u, // deUint32 descriptorSetCount;
425 &*descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts;
426 0u, // deUint32 pushConstantRangeCount;
427 DE_NULL // const VkPushConstantRange* pPushConstantRanges;
428 };
429 vk::Move<vk::VkPipelineLayout> pipelineLayout (createPipelineLayout(vk, device, &pipelineLayoutParams));
430
431 vk::Move<vk::VkShaderModule> shaderModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("compute"), 0));
432 const vk::VkPipelineShaderStageCreateInfo pipelineShaderStageParams =
433 {
434 vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
435 DE_NULL, // const void* pNext;
436 (vk::VkPipelineShaderStageCreateFlags) 0, // VkPipelineShaderStageCreateFlags flags;
437 vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStage stage;
438 *shaderModule, // VkShaderModule module;
439 "main", // const char* pName;
440 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
441 };
442 const vk::VkComputePipelineCreateInfo pipelineCreateInfo =
443 {
444 vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
445 DE_NULL, // const void* pNext;
446 0, // VkPipelineCreateFlags flags;
447 pipelineShaderStageParams, // VkPipelineShaderStageCreateInfo stage;
448 *pipelineLayout, // VkPipelineLayout layout;
449 DE_NULL, // VkPipeline basePipelineHandle;
450 0, // deInt32 basePipelineIndex;
451 };
452
453 vk::Move<vk::VkPipeline> pipeline (createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo));
454 vk::Move<vk::VkCommandPool> cmdPool (createCommandPool(vk, device, vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
455 vk::Move<vk::VkCommandBuffer> cmdBuffer (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,
462 0u, 1u, &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() << TestLog::Message << "Error: passed = " << resCount
482 << ", 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 deUint32 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 } // MemoryModel
522 } // vkt
523