• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params));
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