1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 Google Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Test Case Skeleton Based on Compute Shaders
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktSpvAsmComputeShaderCase.hpp"
25 
26 #include "deSharedPtr.hpp"
27 
28 #include "vkBuilderUtil.hpp"
29 #include "vkMemUtil.hpp"
30 #include "vkRefUtil.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 
34 namespace
35 {
36 
37 using namespace vk;
38 using std::vector;
39 
40 typedef vkt::SpirVAssembly::AllocationMp			AllocationMp;
41 typedef vkt::SpirVAssembly::AllocationSp			AllocationSp;
42 
43 typedef Unique<VkBuffer>							BufferHandleUp;
44 typedef de::SharedPtr<BufferHandleUp>				BufferHandleSp;
45 
46 /*--------------------------------------------------------------------*//*!
47  * \brief Create storage buffer, allocate and bind memory for the buffer
48  *
49  * The memory is created as host visible and passed back as a vk::Allocation
50  * instance via outMemory.
51  *//*--------------------------------------------------------------------*/
createBufferAndBindMemory(const DeviceInterface & vkdi,const VkDevice & device,Allocator & allocator,size_t numBytes,AllocationMp * outMemory)52 Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
53 {
54 	const VkBufferCreateInfo bufferCreateInfo =
55 	{
56 		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,	// sType
57 		DE_NULL,								// pNext
58 		0u,										// flags
59 		numBytes,								// size
60 		VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,		// usage
61 		VK_SHARING_MODE_EXCLUSIVE,				// sharingMode
62 		0u,										// queueFamilyCount
63 		DE_NULL,								// pQueueFamilyIndices
64 	};
65 
66 	Move<VkBuffer>				buffer			(createBuffer(vkdi, device, &bufferCreateInfo));
67 	const VkMemoryRequirements	requirements	= getBufferMemoryRequirements(vkdi, device, *buffer);
68 	AllocationMp				bufferMemory	= allocator.allocate(requirements, MemoryRequirement::HostVisible);
69 
70 	VK_CHECK(vkdi.bindBufferMemory(device, *buffer, bufferMemory->getMemory(), bufferMemory->getOffset()));
71 	*outMemory = bufferMemory;
72 
73 	return buffer;
74 }
75 
setMemory(const DeviceInterface & vkdi,const VkDevice & device,Allocation * destAlloc,size_t numBytes,const void * data)76 void setMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, const void* data)
77 {
78 	void* const hostPtr = destAlloc->getHostPtr();
79 
80 	deMemcpy((deUint8*)hostPtr, data, numBytes);
81 	flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
82 }
83 
fillMemoryWithValue(const DeviceInterface & vkdi,const VkDevice & device,Allocation * destAlloc,size_t numBytes,deUint8 value)84 void fillMemoryWithValue (const DeviceInterface& vkdi, const VkDevice& device, Allocation* destAlloc, size_t numBytes, deUint8 value)
85 {
86 	void* const hostPtr = destAlloc->getHostPtr();
87 
88 	deMemset((deUint8*)hostPtr, value, numBytes);
89 	flushMappedMemoryRange(vkdi, device, destAlloc->getMemory(), destAlloc->getOffset(), numBytes);
90 }
91 
92 /*--------------------------------------------------------------------*//*!
93  * \brief Create a descriptor set layout with numBindings descriptors
94  *
95  * All descriptors are created for shader storage buffer objects and
96  * compute pipeline.
97  *//*--------------------------------------------------------------------*/
createDescriptorSetLayout(const DeviceInterface & vkdi,const VkDevice & device,size_t numBindings)98 Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, size_t numBindings)
99 {
100 	DescriptorSetLayoutBuilder builder;
101 
102 	for (size_t bindingNdx = 0; bindingNdx < numBindings; ++bindingNdx)
103 		builder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
104 
105 	return builder.build(vkdi, device);
106 }
107 
108 /*--------------------------------------------------------------------*//*!
109  * \brief Create a pipeline layout with one descriptor set
110  *//*--------------------------------------------------------------------*/
createPipelineLayout(const DeviceInterface & vkdi,const VkDevice & device,VkDescriptorSetLayout descriptorSetLayout)111 Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout)
112 {
113 	const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
114 	{
115 		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,	// sType
116 		DE_NULL,										// pNext
117 		(VkPipelineLayoutCreateFlags)0,
118 		1u,												// descriptorSetCount
119 		&descriptorSetLayout,							// pSetLayouts
120 		0u,												// pushConstantRangeCount
121 		DE_NULL,										// pPushConstantRanges
122 	};
123 
124 	return createPipelineLayout(vkdi, device, &pipelineLayoutCreateInfo);
125 }
126 
127 /*--------------------------------------------------------------------*//*!
128  * \brief Create a one-time descriptor pool for one descriptor set
129  *
130  * The pool supports numDescriptors storage buffer descriptors.
131  *//*--------------------------------------------------------------------*/
createDescriptorPool(const DeviceInterface & vkdi,const VkDevice & device,deUint32 numDescriptors)132 inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, deUint32 numDescriptors)
133 {
134 	return DescriptorPoolBuilder()
135 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, numDescriptors)
136 		.build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
137 }
138 
139 /*--------------------------------------------------------------------*//*!
140  * \brief Create a descriptor set
141  *
142  * The descriptor set's layout should contain numViews descriptors.
143  * All the descriptors represent buffer views, and they are sequentially
144  * binded to binding point starting from 0.
145  *//*--------------------------------------------------------------------*/
createDescriptorSet(const DeviceInterface & vkdi,const VkDevice & device,VkDescriptorPool pool,VkDescriptorSetLayout layout,size_t numViews,const vector<VkDescriptorBufferInfo> & descriptorInfos)146 Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, size_t numViews, const vector<VkDescriptorBufferInfo>& descriptorInfos)
147 {
148 	const VkDescriptorSetAllocateInfo	allocInfo	=
149 	{
150 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
151 		DE_NULL,
152 		pool,
153 		1u,
154 		&layout
155 	};
156 
157 	Move<VkDescriptorSet>				descriptorSet	= allocateDescriptorSet(vkdi, device, &allocInfo);
158 	DescriptorSetUpdateBuilder			builder;
159 
160 	for (deUint32 descriptorNdx = 0; descriptorNdx < numViews; ++descriptorNdx)
161 		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfos[descriptorNdx]);
162 	builder.update(vkdi, device);
163 
164 	return descriptorSet;
165 }
166 
167 /*--------------------------------------------------------------------*//*!
168  * \brief Create a compute pipeline based on the given shader
169  *//*--------------------------------------------------------------------*/
createComputePipeline(const DeviceInterface & vkdi,const VkDevice & device,VkPipelineLayout pipelineLayout,VkShaderModule shader,const char * entryPoint,const vector<deUint32> & specConstants)170 Move<VkPipeline> createComputePipeline (const DeviceInterface& vkdi, const VkDevice& device, VkPipelineLayout pipelineLayout, VkShaderModule shader, const char* entryPoint, const vector<deUint32>& specConstants)
171 {
172 	const deUint32							numSpecConstants				= (deUint32)specConstants.size();
173 	vector<VkSpecializationMapEntry>		entries;
174 	VkSpecializationInfo					specInfo;
175 
176 	if (numSpecConstants != 0)
177 	{
178 		entries.resize(numSpecConstants);
179 
180 		for (deUint32 ndx = 0; ndx < numSpecConstants; ++ndx)
181 		{
182 			entries[ndx].constantID	= ndx;
183 			entries[ndx].offset		= ndx * (deUint32)sizeof(deUint32);
184 			entries[ndx].size		= sizeof(deUint32);
185 		}
186 
187 		specInfo.mapEntryCount		= numSpecConstants;
188 		specInfo.pMapEntries		= &entries[0];
189 		specInfo.dataSize			= numSpecConstants * sizeof(deUint32);
190 		specInfo.pData				= specConstants.data();
191 	}
192 
193 	const VkPipelineShaderStageCreateInfo	pipelineShaderStageCreateInfo	=
194 	{
195 		VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	// sType
196 		DE_NULL,												// pNext
197 		(VkPipelineShaderStageCreateFlags)0,					// flags
198 		VK_SHADER_STAGE_COMPUTE_BIT,							// stage
199 		shader,													// module
200 		entryPoint,												// pName
201 		(numSpecConstants == 0) ? DE_NULL : &specInfo,			// pSpecializationInfo
202 	};
203 	const VkComputePipelineCreateInfo		pipelineCreateInfo				=
204 	{
205 		VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,			// sType
206 		DE_NULL,												// pNext
207 		(VkPipelineCreateFlags)0,
208 		pipelineShaderStageCreateInfo,							// cs
209 		pipelineLayout,											// layout
210 		(VkPipeline)0,											// basePipelineHandle
211 		0u,														// basePipelineIndex
212 	};
213 
214 	return createComputePipeline(vkdi, device, (VkPipelineCache)0u, &pipelineCreateInfo);
215 }
216 
217 /*--------------------------------------------------------------------*//*!
218  * \brief Create a command pool
219  *
220  * The created command pool is designated for use on the queue type
221  * represented by the given queueFamilyIndex.
222  *//*--------------------------------------------------------------------*/
createCommandPool(const DeviceInterface & vkdi,VkDevice device,deUint32 queueFamilyIndex)223 Move<VkCommandPool> createCommandPool (const DeviceInterface& vkdi, VkDevice device, deUint32 queueFamilyIndex)
224 {
225 	const VkCommandPoolCreateInfo cmdPoolCreateInfo =
226 	{
227 		VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,	// sType
228 		DE_NULL,									// pNext
229 		0u,											// flags
230 		queueFamilyIndex,							// queueFamilyIndex
231 	};
232 
233 	return createCommandPool(vkdi, device, &cmdPoolCreateInfo);
234 }
235 
236 } // anonymous
237 
238 namespace vkt
239 {
240 namespace SpirVAssembly
241 {
242 
243 /*--------------------------------------------------------------------*//*!
244  * \brief Test instance for compute pipeline
245  *
246  * The compute shader is specified in the format of SPIR-V assembly, which
247  * is allowed to access MAX_NUM_INPUT_BUFFERS input storage buffers and
248  * MAX_NUM_OUTPUT_BUFFERS output storage buffers maximally. The shader
249  * source and input/output data are given in a ComputeShaderSpec object.
250  *
251  * This instance runs the given compute shader by feeding the data from input
252  * buffers and compares the data in the output buffers with the expected.
253  *//*--------------------------------------------------------------------*/
254 class SpvAsmComputeShaderInstance : public TestInstance
255 {
256 public:
257 								SpvAsmComputeShaderInstance	(Context& ctx, const ComputeShaderSpec& spec);
258 	tcu::TestStatus				iterate						(void);
259 
260 private:
261 	const ComputeShaderSpec&	m_shaderSpec;
262 };
263 
264 // ComputeShaderTestCase implementations
265 
SpvAsmComputeShaderCase(tcu::TestContext & testCtx,const char * name,const char * description,const ComputeShaderSpec & spec)266 SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec)
267 	: TestCase		(testCtx, name, description)
268 	, m_shaderSpec	(spec)
269 {
270 }
271 
initPrograms(SourceCollections & programCollection) const272 void SpvAsmComputeShaderCase::initPrograms (SourceCollections& programCollection) const
273 {
274 	programCollection.spirvAsmSources.add("compute") << m_shaderSpec.assembly.c_str();
275 }
276 
createInstance(Context & ctx) const277 TestInstance* SpvAsmComputeShaderCase::createInstance (Context& ctx) const
278 {
279 	return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec);
280 }
281 
282 // ComputeShaderTestInstance implementations
283 
SpvAsmComputeShaderInstance(Context & ctx,const ComputeShaderSpec & spec)284 SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec)
285 	: TestInstance	(ctx)
286 	, m_shaderSpec	(spec)
287 {
288 }
289 
iterate(void)290 tcu::TestStatus SpvAsmComputeShaderInstance::iterate (void)
291 {
292 	const DeviceInterface&				vkdi				= m_context.getDeviceInterface();
293 	const VkDevice&						device				= m_context.getDevice();
294 	Allocator&							allocator			= m_context.getDefaultAllocator();
295 
296 	vector<AllocationSp>				inputAllocs;
297 	vector<AllocationSp>				outputAllocs;
298 	vector<BufferHandleSp>				inputBuffers;
299 	vector<BufferHandleSp>				outputBuffers;
300 	vector<VkDescriptorBufferInfo>		descriptorInfos;
301 
302 	DE_ASSERT(!m_shaderSpec.outputs.empty());
303 	const size_t						numBuffers			= m_shaderSpec.inputs.size() + m_shaderSpec.outputs.size();
304 
305 	// Create buffer object, allocate storage, and create view for all input/output buffers.
306 
307 	for (size_t inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
308 	{
309 		AllocationMp		alloc;
310 		const BufferSp&		input		= m_shaderSpec.inputs[inputNdx];
311 		const size_t		numBytes	= input->getNumBytes();
312 		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
313 
314 		setMemory(vkdi, device, &*alloc, numBytes, input->data());
315 		descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
316 		inputBuffers.push_back(BufferHandleSp(buffer));
317 		inputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
318 	}
319 
320 	for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
321 	{
322 		AllocationMp		alloc;
323 		const BufferSp&		output		= m_shaderSpec.outputs[outputNdx];
324 		const size_t		numBytes	= output->getNumBytes();
325 		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
326 
327 		fillMemoryWithValue(vkdi, device, &*alloc, numBytes, 0xff);
328 		descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
329 		outputBuffers.push_back(BufferHandleSp(buffer));
330 		outputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
331 	}
332 
333 	// Create layouts and descriptor set.
334 
335 	Unique<VkDescriptorSetLayout>		descriptorSetLayout	(createDescriptorSetLayout(vkdi, device, numBuffers));
336 	Unique<VkPipelineLayout>			pipelineLayout		(createPipelineLayout(vkdi, device, *descriptorSetLayout));
337 	Unique<VkDescriptorPool>			descriptorPool		(createDescriptorPool(vkdi, device, (deUint32)numBuffers));
338 	Unique<VkDescriptorSet>				descriptorSet		(createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, numBuffers, descriptorInfos));
339 
340 	// Create compute shader and pipeline.
341 
342 	const ProgramBinary&				binary				= m_context.getBinaryCollection().get("compute");
343 	Unique<VkShaderModule>				module				(createShaderModule(vkdi, device, binary, (VkShaderModuleCreateFlags)0u));
344 
345 	Unique<VkPipeline>					computePipeline		(createComputePipeline(vkdi, device, *pipelineLayout, *module, m_shaderSpec.entryPoint.c_str(), m_shaderSpec.specConstants));
346 
347 	// Create command buffer and record commands
348 
349 	const Unique<VkCommandPool>			cmdPool				(createCommandPool(vkdi, device, m_context.getUniversalQueueFamilyIndex()));
350 	const VkCommandBufferAllocateInfo	cmdBufferCreateInfo	=
351 	{
352 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,	// sType
353 		NULL,											// pNext
354 		*cmdPool,										// cmdPool
355 		VK_COMMAND_BUFFER_LEVEL_PRIMARY,				// level
356 		1u												// count
357 	};
358 
359 	Unique<VkCommandBuffer>				cmdBuffer			(allocateCommandBuffer(vkdi, device, &cmdBufferCreateInfo));
360 
361 	const VkCommandBufferBeginInfo		cmdBufferBeginInfo	=
362 	{
363 		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,	// sType
364 		DE_NULL,										// pNext
365 		VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
366 		(const VkCommandBufferInheritanceInfo*)DE_NULL,
367 	};
368 
369 	const tcu::IVec3&				numWorkGroups		= m_shaderSpec.numWorkGroups;
370 
371 	VK_CHECK(vkdi.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
372 	vkdi.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
373 	vkdi.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
374 	vkdi.cmdDispatch(*cmdBuffer, numWorkGroups.x(), numWorkGroups.y(), numWorkGroups.z());
375 	VK_CHECK(vkdi.endCommandBuffer(*cmdBuffer));
376 
377 	// Create fence and run.
378 
379 	const VkFenceCreateInfo			fenceCreateInfo		=
380 	{
381 		 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,		// sType
382 		 NULL,										// pNext
383 		 0											// flags
384     };
385 	const Unique<VkFence>			cmdCompleteFence	(createFence(vkdi, device, &fenceCreateInfo));
386 	const deUint64					infiniteTimeout		= ~(deUint64)0u;
387 	const VkSubmitInfo				submitInfo			=
388 	{
389 		VK_STRUCTURE_TYPE_SUBMIT_INFO,
390 		DE_NULL,
391 		0u,
392 		(const VkSemaphore*)DE_NULL,
393 		(const VkPipelineStageFlags*)DE_NULL,
394 		1u,
395 		&cmdBuffer.get(),
396 		0u,
397 		(const VkSemaphore*)DE_NULL,
398 	};
399 
400 	VK_CHECK(vkdi.queueSubmit(m_context.getUniversalQueue(), 1, &submitInfo, *cmdCompleteFence));
401 	VK_CHECK(vkdi.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
402 
403 	// Check output.
404 	if (m_shaderSpec.verifyIO)
405 	{
406 		if (!(*m_shaderSpec.verifyIO)(m_shaderSpec.inputs, outputAllocs, m_shaderSpec.outputs))
407 			return tcu::TestStatus::fail("Output doesn't match with expected");
408 	}
409 	else
410 	{
411 		for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
412 		{
413 			const BufferSp& expectedOutput = m_shaderSpec.outputs[outputNdx];
414 			if (deMemCmp(expectedOutput->data(), outputAllocs[outputNdx]->getHostPtr(), expectedOutput->getNumBytes()))
415 				return tcu::TestStatus::fail("Output doesn't match with expected");
416 		}
417 	}
418 
419 	return tcu::TestStatus::pass("Ouput match with expected");
420 }
421 
422 } // SpirVAssembly
423 } // vkt
424