1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group 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 Synchronization primitive tests with single queue
22 *//*--------------------------------------------------------------------*/
23
24#include "vktSynchronizationOperationSingleQueueTests.hpp"
25#include "vkDefs.hpp"
26#include "vktTestCase.hpp"
27#include "vktTestCaseUtil.hpp"
28#include "vktTestGroupUtil.hpp"
29#include "vkRef.hpp"
30#include "vkRefUtil.hpp"
31#include "vkMemUtil.hpp"
32#include "vkQueryUtil.hpp"
33#include "vkTypeUtil.hpp"
34#include "deUniquePtr.hpp"
35#include "tcuTestLog.hpp"
36#include "vktSynchronizationUtil.hpp"
37#include "vktSynchronizationOperation.hpp"
38#include "vktSynchronizationOperationTestData.hpp"
39
40namespace vkt
41{
42namespace synchronization
43{
44namespace
45{
46using namespace vk;
47
48class BaseTestInstance : public TestInstance
49{
50public:
51	BaseTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
52		: TestInstance	(context)
53		, m_opContext	(context, pipelineCacheData)
54		, m_resource	(new Resource(m_opContext, resourceDesc, writeOp.getResourceUsageFlags() | readOp.getResourceUsageFlags()))
55		, m_writeOp		(writeOp.build(m_opContext, *m_resource))
56		, m_readOp		(readOp.build(m_opContext, *m_resource))
57	{
58	}
59
60protected:
61	OperationContext					m_opContext;
62	const de::UniquePtr<Resource>		m_resource;
63	const de::UniquePtr<Operation>		m_writeOp;
64	const de::UniquePtr<Operation>		m_readOp;
65};
66
67class EventTestInstance : public BaseTestInstance
68{
69public:
70	EventTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
71		: BaseTestInstance		(context, resourceDesc, writeOp, readOp, pipelineCacheData)
72	{
73	}
74
75	tcu::TestStatus iterate (void)
76	{
77		const DeviceInterface&			vk					= m_context.getDeviceInterface();
78		const VkDevice					device				= m_context.getDevice();
79		const VkQueue					queue				= m_context.getUniversalQueue();
80		const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
81		const Unique<VkCommandPool>		cmdPool				(makeCommandPool(vk, device, queueFamilyIndex));
82		const Unique<VkCommandBuffer>	cmdBuffer			(makeCommandBuffer(vk, device, *cmdPool));
83		const Unique<VkEvent>			event				(makeEvent(vk, device));
84		const SyncInfo					writeSync			= m_writeOp->getSyncInfo();
85		const SyncInfo					readSync			= m_readOp->getSyncInfo();
86
87		beginCommandBuffer(vk, *cmdBuffer);
88
89		m_writeOp->recordCommands(*cmdBuffer);
90		vk.cmdSetEvent(*cmdBuffer, *event, writeSync.stageMask);
91
92		if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
93		{
94			const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
95				m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
96			vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 1u, &barrier, 0u, DE_NULL);
97		}
98		else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
99		{
100			const VkImageMemoryBarrier barrier =  makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
101				writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
102			vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier);
103		}
104
105		m_readOp->recordCommands(*cmdBuffer);
106
107		endCommandBuffer(vk, *cmdBuffer);
108		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
109
110		{
111			const Data	expected = m_writeOp->getData();
112			const Data	actual	 = m_readOp->getData();
113
114			if (0 != deMemCmp(expected.data, actual.data, expected.size))
115				return tcu::TestStatus::fail("Memory contents don't match");
116		}
117
118		return tcu::TestStatus::pass("OK");
119	}
120};
121
122class BarrierTestInstance : public BaseTestInstance
123{
124public:
125	BarrierTestInstance	(Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
126		: BaseTestInstance		(context, resourceDesc, writeOp, readOp, pipelineCacheData)
127	{
128	}
129
130	tcu::TestStatus iterate (void)
131	{
132		const DeviceInterface&			vk					= m_context.getDeviceInterface();
133		const VkDevice					device				= m_context.getDevice();
134		const VkQueue					queue				= m_context.getUniversalQueue();
135		const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
136		const Unique<VkCommandPool>		cmdPool				(makeCommandPool(vk, device, queueFamilyIndex));
137		const Move<VkCommandBuffer>		cmdBuffer			(makeCommandBuffer(vk, device, *cmdPool));
138		const SyncInfo					writeSync			= m_writeOp->getSyncInfo();
139		const SyncInfo					readSync			= m_readOp->getSyncInfo();
140
141		beginCommandBuffer(vk, *cmdBuffer);
142
143		m_writeOp->recordCommands(*cmdBuffer);
144
145		if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
146		{
147			const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
148				m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
149			vk.cmdPipelineBarrier(*cmdBuffer,  writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
150		}
151		else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
152		{
153			const VkImageMemoryBarrier barrier =  makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
154				writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
155			vk.cmdPipelineBarrier(*cmdBuffer,  writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
156		}
157
158		m_readOp->recordCommands(*cmdBuffer);
159
160		endCommandBuffer(vk, *cmdBuffer);
161
162		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
163
164		{
165			const Data	expected = m_writeOp->getData();
166			const Data	actual	 = m_readOp->getData();
167
168			if (0 != deMemCmp(expected.data, actual.data, expected.size))
169				return tcu::TestStatus::fail("Memory contents don't match");
170		}
171
172		return tcu::TestStatus::pass("OK");
173	}
174};
175
176class SemaphoreTestInstance : public BaseTestInstance
177{
178public:
179	SemaphoreTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
180		: BaseTestInstance	(context, resourceDesc, writeOp, readOp, pipelineCacheData)
181	{
182	}
183
184	tcu::TestStatus	iterate (void)
185	{
186		enum {WRITE=0, READ, COUNT};
187		const DeviceInterface&			vk					= m_context.getDeviceInterface();
188		const VkDevice					device				= m_context.getDevice();
189		const VkQueue					queue				= m_context.getUniversalQueue();
190		const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
191		const VkSemaphoreCreateInfo		semaphoreInfo		=
192															{
193																VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,	//VkStructureType			sType;
194																DE_NULL,									//const void*				pNext;
195																0u											//VkSemaphoreCreateFlags	flags;
196															};
197		const Unique<VkSemaphore>		semaphore			(createSemaphore (vk, device, &semaphoreInfo, DE_NULL));
198		const Unique<VkCommandPool>		cmdPool				(makeCommandPool(vk, device, queueFamilyIndex));
199		const Move<VkCommandBuffer>		ptrCmdBuffer[COUNT]	= {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
200		VkCommandBuffer					cmdBuffers[COUNT]	= {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
201		const VkPipelineStageFlags		stageBits[]			= { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
202		const VkSubmitInfo				submitInfo[2]		=
203															{
204																{
205																	VK_STRUCTURE_TYPE_SUBMIT_INFO,		// VkStructureType			sType;
206																	DE_NULL,							// const void*				pNext;
207																	0u,									// deUint32					waitSemaphoreCount;
208																	DE_NULL,							// const VkSemaphore*		pWaitSemaphores;
209																	(const VkPipelineStageFlags*)DE_NULL,
210																	1u,									// deUint32					commandBufferCount;
211																	&cmdBuffers[WRITE],					// const VkCommandBuffer*	pCommandBuffers;
212																	1u,									// deUint32					signalSemaphoreCount;
213																	&semaphore.get(),					// const VkSemaphore*		pSignalSemaphores;
214																},
215																{
216																	VK_STRUCTURE_TYPE_SUBMIT_INFO,		// VkStructureType				sType;
217																	DE_NULL,							// const void*					pNext;
218																	1u,									// deUint32						waitSemaphoreCount;
219																	&semaphore.get(),					// const VkSemaphore*			pWaitSemaphores;
220																	stageBits,							// const VkPipelineStageFlags*	pWaitDstStageMask;
221																	1u,									// deUint32						commandBufferCount;
222																	&cmdBuffers[READ],					// const VkCommandBuffer*		pCommandBuffers;
223																	0u,									// deUint32						signalSemaphoreCount;
224																	DE_NULL,							// const VkSemaphore*			pSignalSemaphores;
225																}
226															};
227		const SyncInfo					writeSync			= m_writeOp->getSyncInfo();
228		const SyncInfo					readSync			= m_readOp->getSyncInfo();
229
230		beginCommandBuffer(vk, cmdBuffers[WRITE]);
231
232		m_writeOp->recordCommands(cmdBuffers[WRITE]);
233
234		if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
235		{
236			const VkImageMemoryBarrier barrier =  makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
237				writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
238			vk.cmdPipelineBarrier(cmdBuffers[WRITE],  writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
239		}
240
241		endCommandBuffer(vk, cmdBuffers[WRITE]);
242
243		beginCommandBuffer(vk, cmdBuffers[READ]);
244
245		m_readOp->recordCommands(cmdBuffers[READ]);
246
247		endCommandBuffer(vk, cmdBuffers[READ]);
248
249		VK_CHECK(vk.queueSubmit(queue, 2u, submitInfo, DE_NULL));
250		VK_CHECK(vk.queueWaitIdle(queue));
251
252		{
253			const Data	expected = m_writeOp->getData();
254			const Data	actual	 = m_readOp->getData();
255
256			if (0 != deMemCmp(expected.data, actual.data, expected.size))
257				return tcu::TestStatus::fail("Memory contents don't match");
258		}
259
260		return tcu::TestStatus::pass("OK");
261	}
262};
263
264class FenceTestInstance : public BaseTestInstance
265{
266public:
267	FenceTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
268		: BaseTestInstance	(context, resourceDesc, writeOp, readOp, pipelineCacheData)
269	{
270	}
271
272	tcu::TestStatus	iterate (void)
273	{
274		enum {WRITE=0, READ, COUNT};
275		const DeviceInterface&			vk					= m_context.getDeviceInterface();
276		const VkDevice					device				= m_context.getDevice();
277		const VkQueue					queue				= m_context.getUniversalQueue();
278		const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
279		const Unique<VkCommandPool>		cmdPool				(makeCommandPool(vk, device, queueFamilyIndex));
280		const Move<VkCommandBuffer>		ptrCmdBuffer[COUNT]	= {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
281		VkCommandBuffer					cmdBuffers[COUNT]	= {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
282		const SyncInfo					writeSync			= m_writeOp->getSyncInfo();
283		const SyncInfo					readSync			= m_readOp->getSyncInfo();
284
285		beginCommandBuffer(vk, cmdBuffers[WRITE]);
286
287		m_writeOp->recordCommands(cmdBuffers[WRITE]);
288
289		if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
290		{
291			const VkImageMemoryBarrier barrier =  makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
292				writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
293			vk.cmdPipelineBarrier(cmdBuffers[WRITE],  writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
294		}
295
296		endCommandBuffer(vk, cmdBuffers[WRITE]);
297
298		submitCommandsAndWait(vk, device, queue, cmdBuffers[WRITE]);
299
300		beginCommandBuffer(vk, cmdBuffers[READ]);
301
302		m_readOp->recordCommands(cmdBuffers[READ]);
303
304		endCommandBuffer(vk, cmdBuffers[READ]);
305
306		submitCommandsAndWait(vk, device, queue, cmdBuffers[READ]);
307
308		{
309			const Data	expected = m_writeOp->getData();
310			const Data	actual	 = m_readOp->getData();
311
312			if (0 != deMemCmp(expected.data, actual.data, expected.size))
313				return tcu::TestStatus::fail("Memory contents don't match");
314		}
315
316		return tcu::TestStatus::pass("OK");
317	}
318};
319
320class SyncTestCase : public TestCase
321{
322public:
323	SyncTestCase	(tcu::TestContext&			testCtx,
324					 const std::string&			name,
325					 const std::string&			description,
326					 const SyncPrimitive		syncPrimitive,
327					 const ResourceDescription	resourceDesc,
328					 const OperationName		writeOp,
329					 const OperationName		readOp,
330					 PipelineCacheData&			pipelineCacheData)
331		: TestCase				(testCtx, name, description)
332		, m_resourceDesc		(resourceDesc)
333		, m_writeOp				(makeOperationSupport(writeOp, resourceDesc))
334		, m_readOp				(makeOperationSupport(readOp, resourceDesc))
335		, m_syncPrimitive		(syncPrimitive)
336		, m_pipelineCacheData	(pipelineCacheData)
337	{
338	}
339
340	void initPrograms (SourceCollections& programCollection) const
341	{
342		m_writeOp->initPrograms(programCollection);
343		m_readOp->initPrograms(programCollection);
344	}
345
346	TestInstance* createInstance (Context& context) const
347	{
348		switch (m_syncPrimitive)
349		{
350			case SYNC_PRIMITIVE_FENCE:
351				return new FenceTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
352			case SYNC_PRIMITIVE_SEMAPHORE:
353				return new SemaphoreTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
354			case SYNC_PRIMITIVE_BARRIER:
355				return new BarrierTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
356			case SYNC_PRIMITIVE_EVENT:
357				return new EventTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
358		}
359
360		DE_ASSERT(0);
361		return DE_NULL;
362	}
363
364private:
365	const ResourceDescription				m_resourceDesc;
366	const de::UniquePtr<OperationSupport>	m_writeOp;
367	const de::UniquePtr<OperationSupport>	m_readOp;
368	const SyncPrimitive						m_syncPrimitive;
369	PipelineCacheData&						m_pipelineCacheData;
370};
371
372void createTests (tcu::TestCaseGroup* group, PipelineCacheData* pipelineCacheData)
373{
374	tcu::TestContext& testCtx = group->getTestContext();
375
376	static const struct
377	{
378		const char*		name;
379		SyncPrimitive	syncPrimitive;
380		int				numOptions;
381	} groups[] =
382	{
383		{ "fence",		SYNC_PRIMITIVE_FENCE,		0, },
384		{ "semaphore",	SYNC_PRIMITIVE_SEMAPHORE,	0, },
385		{ "barrier",	SYNC_PRIMITIVE_BARRIER,		1, },
386		{ "event",		SYNC_PRIMITIVE_EVENT,		1, },
387	};
388
389	for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx)
390	{
391		de::MovePtr<tcu::TestCaseGroup> synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name, ""));
392
393		for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx)
394		for (int readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx)
395		{
396			const OperationName	writeOp		= s_writeOps[writeOpNdx];
397			const OperationName	readOp		= s_readOps[readOpNdx];
398			const std::string	opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp);
399			bool				empty		= true;
400
401			de::MovePtr<tcu::TestCaseGroup> opGroup	(new tcu::TestCaseGroup(testCtx, opGroupName.c_str(), ""));
402
403			for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx)
404			{
405				const ResourceDescription&	resource	= s_resources[resourceNdx];
406				std::string					name		= getResourceName(resource);
407
408				if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource))
409				{
410					opGroup->addChild(new SyncTestCase(testCtx, name, "", groups[groupNdx].syncPrimitive, resource, writeOp, readOp, *pipelineCacheData));
411					empty = false;
412				}
413			}
414			if (!empty)
415				synchGroup->addChild(opGroup.release());
416		}
417
418		group->addChild(synchGroup.release());
419	}
420}
421
422} // anonymous
423
424tcu::TestCaseGroup* createSynchronizedOperationSingleQueueTests (tcu::TestContext& testCtx, PipelineCacheData& pipelineCacheData)
425{
426	return createTestGroup(testCtx, "single_queue", "Synchronization of a memory-modifying operation", createTests, &pipelineCacheData);
427}
428
429} // synchronization
430} // vkt
431