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 Queue bind sparse tests
22 *//*--------------------------------------------------------------------*/
23
24#include "vktSparseResourcesQueueBindSparseTests.hpp"
25#include "vktSparseResourcesTestsUtil.hpp"
26#include "vktSparseResourcesBase.hpp"
27#include "vktTestGroupUtil.hpp"
28
29#include "vkDefs.hpp"
30#include "vkRefUtil.hpp"
31#include "vkMemUtil.hpp"
32#include "vkTypeUtil.hpp"
33#include "vkQueryUtil.hpp"
34
35#include "deUniquePtr.hpp"
36#include "deSharedPtr.hpp"
37
38#include <string>
39#include <vector>
40
41using namespace vk;
42using de::MovePtr;
43
44namespace vkt
45{
46namespace sparse
47{
48namespace
49{
50
51typedef de::SharedPtr<Unique<VkSemaphore> >	SemaphoreSp;
52typedef de::SharedPtr<Unique<VkFence> >		FenceSp;
53
54struct TestParams
55{
56	deUint32	numQueues;					//! use 2 or more to sync between different queues
57	deUint32	numWaitSemaphores;
58	deUint32	numSignalSemaphores;
59	bool		emptySubmission;			//! will make an empty bind sparse submission
60	bool		bindSparseUseFence;
61};
62
63struct QueueSubmission
64{
65	union InfoUnion
66	{
67		VkSubmitInfo		regular;
68		VkBindSparseInfo	sparse;
69	};
70
71	const Queue*			queue;
72	bool					isSparseBinding;
73	InfoUnion				info;
74};
75
76QueueSubmission makeSubmissionRegular (const Queue*					queue,
77									   const deUint32				numWaitSemaphores,
78									   const VkSemaphore*			pWaitSemaphore,
79									   const VkPipelineStageFlags*	pWaitDstStageMask,
80									   const deUint32				numSignalSemaphores,
81									   const VkSemaphore*			pSignalSemaphore)
82{
83	const VkSubmitInfo submitInfo =
84	{
85		VK_STRUCTURE_TYPE_SUBMIT_INFO,				// VkStructureType                sType;
86		DE_NULL,									// const void*                    pNext;
87		numWaitSemaphores,							// uint32_t                       waitSemaphoreCount;
88		pWaitSemaphore,								// const VkSemaphore*             pWaitSemaphores;
89		pWaitDstStageMask,							// const VkPipelineStageFlags*    pWaitDstStageMask;
90		0u,											// uint32_t                       commandBufferCount;
91		DE_NULL,									// const VkCommandBuffer*         pCommandBuffers;
92		numSignalSemaphores,						// uint32_t                       signalSemaphoreCount;
93		pSignalSemaphore,							// const VkSemaphore*             pSignalSemaphores;
94	};
95
96	QueueSubmission submission;
97	submission.isSparseBinding	= false;
98	submission.queue			= queue;
99	submission.info.regular		= submitInfo;
100
101	return submission;
102}
103
104QueueSubmission makeSubmissionSparse (const Queue*			queue,
105									  const deUint32		numWaitSemaphores,
106									  const VkSemaphore*	pWaitSemaphore,
107									  const deUint32		numSignalSemaphores,
108									  const VkSemaphore*	pSignalSemaphore)
109{
110	const VkBindSparseInfo bindInfo =
111	{
112		VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,			// VkStructureType                             sType;
113		DE_NULL,									// const void*                                 pNext;
114		numWaitSemaphores,							// uint32_t                                    waitSemaphoreCount;
115		pWaitSemaphore,								// const VkSemaphore*                          pWaitSemaphores;
116		0u,											// uint32_t                                    bufferBindCount;
117		DE_NULL,									// const VkSparseBufferMemoryBindInfo*         pBufferBinds;
118		0u,											// uint32_t                                    imageOpaqueBindCount;
119		DE_NULL,									// const VkSparseImageOpaqueMemoryBindInfo*    pImageOpaqueBinds;
120		0u,											// uint32_t                                    imageBindCount;
121		DE_NULL,									// const VkSparseImageMemoryBindInfo*          pImageBinds;
122		numSignalSemaphores,						// uint32_t                                    signalSemaphoreCount;
123		pSignalSemaphore,							// const VkSemaphore*                          pSignalSemaphores;
124	};
125
126	QueueSubmission submission;
127	submission.isSparseBinding	= true;
128	submission.queue			= queue;
129	submission.info.sparse		= bindInfo;
130
131	return submission;
132}
133
134bool waitForFences (const DeviceInterface& vk, const VkDevice device, const std::vector<FenceSp>& fences)
135{
136	for (std::vector<FenceSp>::const_iterator fenceSpIter = fences.begin(); fenceSpIter != fences.end(); ++fenceSpIter)
137	{
138		if (vk.waitForFences(device, 1u, &(***fenceSpIter), VK_TRUE, ~0ull) != VK_SUCCESS)
139			return false;
140	}
141	return true;
142}
143
144class SparseQueueBindTestInstance : public SparseResourcesBaseInstance
145{
146public:
147	SparseQueueBindTestInstance (Context &context, const TestParams& params)
148		: SparseResourcesBaseInstance	(context)
149		, m_params						(params)
150	{
151		DE_ASSERT(m_params.numQueues > 0u);		// must use at least one queue
152		DE_ASSERT(!m_params.emptySubmission || (m_params.numWaitSemaphores == 0u && m_params.numSignalSemaphores == 0u));	// can't use semaphores if we don't submit
153	}
154
155	tcu::TestStatus iterate (void)
156	{
157		const InstanceInterface&	vki				= m_context.getInstanceInterface();
158		const VkPhysicalDevice		physDevice		= m_context.getPhysicalDevice();
159		const Queue*				sparseQueue		= DE_NULL;
160		std::vector<const Queue*>	otherQueues;
161
162		if (!getPhysicalDeviceFeatures(vki, physDevice).sparseBinding)
163			TCU_THROW(NotSupportedError, "Sparse binding not supported");
164
165		// Determine required queues and create a device that supports them
166		{
167			QueueRequirementsVec requirements;
168			requirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
169			requirements.push_back(QueueRequirements((VkQueueFlags)0, m_params.numQueues));		// any queue flags
170
171			createDeviceSupportingQueues(requirements);
172
173			sparseQueue = &getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0u);
174
175			// We probably have picked the sparse queue again, so filter it out
176			for (deUint32 queueNdx = 0u; queueNdx < m_params.numQueues; ++queueNdx)
177			{
178				const Queue* queue = &getQueue((VkQueueFlags)0, queueNdx);
179				if (queue->queueHandle != sparseQueue->queueHandle)
180					otherQueues.push_back(queue);
181			}
182		}
183
184		const DeviceInterface&				vk = getDeviceInterface();
185
186		std::vector<SemaphoreSp>			allSemaphores;
187		std::vector<VkSemaphore>			waitSemaphores;
188		std::vector<VkSemaphore>			signalSemaphores;
189		std::vector<VkPipelineStageFlags>	signalSemaphoresWaitDstStageMask;
190		std::vector<QueueSubmission>		queueSubmissions;
191
192		for (deUint32 i = 0; i < m_params.numWaitSemaphores; ++i)
193		{
194			allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
195			waitSemaphores.push_back(**allSemaphores.back());
196		}
197
198		for (deUint32 i = 0; i < m_params.numSignalSemaphores; ++i)
199		{
200			allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
201			signalSemaphores.push_back(**allSemaphores.back());
202			signalSemaphoresWaitDstStageMask.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
203		}
204
205		// Prepare submissions: signal semaphores for the sparse bind operation
206		{
207			deUint32 numQueues		= 1u + static_cast<deUint32>(otherQueues.size());
208			deUint32 numSemaphores	= m_params.numWaitSemaphores;
209
210			while (numSemaphores > 0u && numQueues > 0u)
211			{
212				if (numQueues == 1u)	// sparse queue is assigned last
213				{
214					// sparse queue can handle regular submissions as well
215					queueSubmissions.push_back(makeSubmissionRegular(
216						sparseQueue, 0u, DE_NULL, DE_NULL, numSemaphores, getDataOrNullptr(waitSemaphores)));
217					numSemaphores	= 0u;
218					numQueues		= 0u;
219				}
220				else
221				{
222					queueSubmissions.push_back(makeSubmissionRegular(
223						otherQueues[numQueues - 2], 0u, DE_NULL, DE_NULL, 1u, getDataOrNullptr(waitSemaphores, numSemaphores - 1)));
224					--numQueues;
225					--numSemaphores;
226				}
227			}
228		}
229
230		// Prepare submission: bind sparse
231		if (!m_params.emptySubmission)
232		{
233			queueSubmissions.push_back(makeSubmissionSparse(
234				sparseQueue, m_params.numWaitSemaphores, getDataOrNullptr(waitSemaphores), m_params.numSignalSemaphores, getDataOrNullptr(signalSemaphores)));
235		}
236		else
237		{
238			// a dummy submission, won't be used in a call to vkQueueBindSparse
239			queueSubmissions.push_back(makeSubmissionSparse(sparseQueue, 0u, DE_NULL, 0u, DE_NULL));
240		}
241
242		// Prepare submissions: wait on semaphores signaled by the sparse bind operation
243		if (!m_params.emptySubmission)
244		{
245			deUint32 numQueues		= 1u + static_cast<deUint32>(otherQueues.size());
246			deUint32 numSemaphores	= m_params.numSignalSemaphores;
247
248			while (numSemaphores > 0u && numQueues > 0u)
249			{
250				if (numQueues == 1u)
251				{
252					queueSubmissions.push_back(makeSubmissionRegular(
253						sparseQueue, numSemaphores, getDataOrNullptr(signalSemaphores), getDataOrNullptr(signalSemaphoresWaitDstStageMask), 0u, DE_NULL));
254					numSemaphores	= 0u;
255					numQueues		= 0u;
256				}
257				else
258				{
259					queueSubmissions.push_back(makeSubmissionRegular(
260						otherQueues[numQueues - 2], 1u, getDataOrNullptr(signalSemaphores, numSemaphores - 1), getDataOrNullptr(signalSemaphoresWaitDstStageMask, numSemaphores - 1), 0u, DE_NULL));
261					--numQueues;
262					--numSemaphores;
263				}
264			}
265		}
266
267		// Submit to queues
268		{
269			std::vector<FenceSp>	regularFences;
270			std::vector<FenceSp>	bindSparseFences;
271
272			for (std::vector<QueueSubmission>::const_iterator submissionIter = queueSubmissions.begin(); submissionIter != queueSubmissions.end(); ++submissionIter)
273			{
274				if (submissionIter->isSparseBinding)
275				{
276					VkFence fence = DE_NULL;
277
278					if (m_params.bindSparseUseFence)
279					{
280						bindSparseFences.push_back(makeVkSharedPtr(createFence(vk, getDevice())));
281						fence = **bindSparseFences.back();
282					}
283
284					if (m_params.emptySubmission)
285						VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 0u, DE_NULL, fence));
286					else
287						VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 1u, &submissionIter->info.sparse, fence));
288				}
289				else
290				{
291					regularFences.push_back(makeVkSharedPtr(createFence(vk, getDevice())));
292					VK_CHECK(vk.queueSubmit(submissionIter->queue->queueHandle, 1u, &submissionIter->info.regular, **regularFences.back()));
293				}
294			}
295
296			if (!waitForFences(vk, getDevice(), bindSparseFences))
297				return tcu::TestStatus::fail("vkQueueBindSparse didn't signal the fence");
298
299			if (!waitForFences(vk, getDevice(), regularFences))
300				return tcu::TestStatus::fail("Some fences weren't signaled (vkQueueBindSparse didn't signal semaphores?)");
301		}
302
303		// May return an error if some waitSemaphores didn't get signaled
304		VK_CHECK(vk.deviceWaitIdle(getDevice()));
305
306		return tcu::TestStatus::pass("Pass");
307	}
308
309private:
310	const TestParams	m_params;
311};
312
313class SparseQueueBindTest : public TestCase
314{
315public:
316	SparseQueueBindTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
317		: TestCase	(testCtx, name, description)
318		, m_params	(params)
319	{
320		DE_ASSERT(params.numQueues > 0u);
321		DE_ASSERT(params.numQueues == 1u || m_params.numWaitSemaphores > 0u || m_params.numSignalSemaphores > 0u);	// without any semaphores, only sparse queue will be used
322	}
323
324	TestInstance* createInstance (Context& context) const
325	{
326		return new SparseQueueBindTestInstance(context, m_params);
327	}
328
329private:
330	const TestParams	m_params;
331};
332
333void populateTestGroup(tcu::TestCaseGroup* group)
334{
335	const struct
336	{
337		std::string		name;
338		TestParams		params;
339		std::string		description;
340	} cases[] =
341	{
342		// case name									// numQueues, numWaitSems, numSignalSems, emptySubmission, checkFence
343		{ "no_dependency",								{	1u,	0u,	0u,	false,	false,	}, "submit without any semaphores", },
344		{ "no_dependency_fence",						{	1u,	0u,	0u,	false,	true,	}, "submit without any semaphores, signal a fence", },
345
346		{ "single_queue_wait_one",						{	1u,	1u,	0u,	false,	true,	}, "only sparse queue, wait for semaphore(s)", },
347		{ "single_queue_wait_many",						{	1u,	3u,	0u,	false,	true,	}, "only sparse queue, wait for semaphore(s)", },
348		{ "single_queue_signal_one",					{	1u,	0u,	1u,	false,	true,	}, "only sparse queue, signal semaphore(s)", },
349		{ "single_queue_signal_many",					{	1u,	0u,	3u,	false,	true,	}, "only sparse queue, signal semaphore(s)", },
350		{ "single_queue_wait_one_signal_one",			{	1u,	1u,	1u,	false,	true,	}, "only sparse queue, wait for and signal semaphore(s)", },
351		{ "single_queue_wait_many_signal_many",			{	1u,	2u,	3u,	false,	true,	}, "only sparse queue, wait for and signal semaphore(s)", },
352
353		{ "multi_queue_wait_one",						{	2u,	1u,	0u,	false,	true,	}, "sparse and other queues, wait for semaphore(s)", },
354		{ "multi_queue_wait_many",						{	2u,	2u,	0u,	false,	true,	}, "sparse and other queues, wait for semaphore(s)", },
355		{ "multi_queue_signal_one",						{	2u,	0u,	1u,	false,	true,	}, "sparse and other queues, signal semaphore(s)", },
356		{ "multi_queue_signal_many",					{	2u,	0u,	2u,	false,	true,	}, "sparse and other queues, signal semaphore(s)", },
357		{ "multi_queue_wait_one_signal_one",			{	2u,	1u,	1u,	false,	true,	}, "sparse and other queues, wait for and signal semaphore(s)", },
358		{ "multi_queue_wait_many_signal_many",			{	2u,	2u,	2u,	false,	true,	}, "sparse and other queues, wait for and signal semaphore(s)", },
359		{ "multi_queue_wait_one_signal_one_other",		{	2u,	1u,	1u,	false,	true,	}, "sparse and other queues, wait for and signal semaphore(s) on other queues", },
360		{ "multi_queue_wait_many_signal_many_other",	{	3u,	2u,	2u,	false,	true,	}, "sparse and other queues, wait for and signal semaphore(s) on other queues", },
361
362		{ "empty",										{	1u,	0u,	0u,	true,	false,	}, "call vkQueueBindSparse with zero bindInfos", },
363		{ "empty_fence",								{	1u,	0u,	0u,	true,	true,	}, "call vkQueueBindSparse with zero bindInfos, signal a fence", },
364	};
365
366	for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
367		group->addChild(new SparseQueueBindTest(group->getTestContext(), cases[caseNdx].name, cases[caseNdx].description, cases[caseNdx].params));
368}
369
370} // anonymous ns
371
372//! Sparse queue binding edge cases and synchronization with semaphores/fences.
373//! Actual binding and usage is tested by other test groups.
374tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx)
375{
376	return createTestGroup(testCtx, "queue_bind", "Queue bind sparse tests", populateTestGroup);
377}
378
379} // sparse
380} // vkt
381