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 Queue*				sparseQueue	= DE_NULL;
158		std::vector<const Queue*>	otherQueues;
159
160		// Determine required queues and create a device that supports them
161		{
162			QueueRequirementsVec requirements;
163			requirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
164			requirements.push_back(QueueRequirements((VkQueueFlags)0, m_params.numQueues));		// any queue flags
165
166			createDeviceSupportingQueues(requirements);
167
168			sparseQueue = &getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0u);
169
170			// We probably have picked the sparse queue again, so filter it out
171			for (deUint32 queueNdx = 0u; queueNdx < m_params.numQueues; ++queueNdx)
172			{
173				const Queue* queue = &getQueue((VkQueueFlags)0, queueNdx);
174				if (queue->queueHandle != sparseQueue->queueHandle)
175					otherQueues.push_back(queue);
176			}
177		}
178
179		const DeviceInterface&				vk = getDeviceInterface();
180
181		std::vector<SemaphoreSp>			allSemaphores;
182		std::vector<VkSemaphore>			waitSemaphores;
183		std::vector<VkSemaphore>			signalSemaphores;
184		std::vector<VkPipelineStageFlags>	signalSemaphoresWaitDstStageMask;
185		std::vector<QueueSubmission>		queueSubmissions;
186
187		for (deUint32 i = 0; i < m_params.numWaitSemaphores; ++i)
188		{
189			allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
190			waitSemaphores.push_back(**allSemaphores.back());
191		}
192
193		for (deUint32 i = 0; i < m_params.numSignalSemaphores; ++i)
194		{
195			allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
196			signalSemaphores.push_back(**allSemaphores.back());
197			signalSemaphoresWaitDstStageMask.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
198		}
199
200		// Prepare submissions: signal semaphores for the sparse bind operation
201		{
202			deUint32 numQueues		= 1u + static_cast<deUint32>(otherQueues.size());
203			deUint32 numSemaphores	= m_params.numWaitSemaphores;
204
205			while (numSemaphores > 0u && numQueues > 0u)
206			{
207				if (numQueues == 1u)	// sparse queue is assigned last
208				{
209					// sparse queue can handle regular submissions as well
210					queueSubmissions.push_back(makeSubmissionRegular(
211						sparseQueue, 0u, DE_NULL, DE_NULL, numSemaphores, getDataOrNullptr(waitSemaphores)));
212					numSemaphores	= 0u;
213					numQueues		= 0u;
214				}
215				else
216				{
217					queueSubmissions.push_back(makeSubmissionRegular(
218						otherQueues[numQueues - 2], 0u, DE_NULL, DE_NULL, 1u, getDataOrNullptr(waitSemaphores, numSemaphores - 1)));
219					--numQueues;
220					--numSemaphores;
221				}
222			}
223		}
224
225		// Prepare submission: bind sparse
226		if (!m_params.emptySubmission)
227		{
228			queueSubmissions.push_back(makeSubmissionSparse(
229				sparseQueue, m_params.numWaitSemaphores, getDataOrNullptr(waitSemaphores), m_params.numSignalSemaphores, getDataOrNullptr(signalSemaphores)));
230		}
231		else
232		{
233			// an unused submission, won't be used in a call to vkQueueBindSparse
234			queueSubmissions.push_back(makeSubmissionSparse(sparseQueue, 0u, DE_NULL, 0u, DE_NULL));
235		}
236
237		// Prepare submissions: wait on semaphores signaled by the sparse bind operation
238		if (!m_params.emptySubmission)
239		{
240			deUint32 numQueues		= 1u + static_cast<deUint32>(otherQueues.size());
241			deUint32 numSemaphores	= m_params.numSignalSemaphores;
242
243			while (numSemaphores > 0u && numQueues > 0u)
244			{
245				if (numQueues == 1u)
246				{
247					queueSubmissions.push_back(makeSubmissionRegular(
248						sparseQueue, numSemaphores, getDataOrNullptr(signalSemaphores), getDataOrNullptr(signalSemaphoresWaitDstStageMask), 0u, DE_NULL));
249					numSemaphores	= 0u;
250					numQueues		= 0u;
251				}
252				else
253				{
254					queueSubmissions.push_back(makeSubmissionRegular(
255						otherQueues[numQueues - 2], 1u, getDataOrNullptr(signalSemaphores, numSemaphores - 1), getDataOrNullptr(signalSemaphoresWaitDstStageMask, numSemaphores - 1), 0u, DE_NULL));
256					--numQueues;
257					--numSemaphores;
258				}
259			}
260		}
261
262		// Submit to queues
263		{
264			std::vector<FenceSp>	regularFences;
265			std::vector<FenceSp>	bindSparseFences;
266
267			for (std::vector<QueueSubmission>::const_iterator submissionIter = queueSubmissions.begin(); submissionIter != queueSubmissions.end(); ++submissionIter)
268			{
269				if (submissionIter->isSparseBinding)
270				{
271					VkFence fence = DE_NULL;
272
273					if (m_params.bindSparseUseFence)
274					{
275						bindSparseFences.push_back(makeVkSharedPtr(createFence(vk, getDevice())));
276						fence = **bindSparseFences.back();
277					}
278
279					if (m_params.emptySubmission)
280						VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 0u, DE_NULL, fence));
281					else
282						VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 1u, &submissionIter->info.sparse, fence));
283				}
284				else
285				{
286					regularFences.push_back(makeVkSharedPtr(createFence(vk, getDevice())));
287					VK_CHECK(vk.queueSubmit(submissionIter->queue->queueHandle, 1u, &submissionIter->info.regular, **regularFences.back()));
288				}
289			}
290
291			if (!waitForFences(vk, getDevice(), bindSparseFences))
292				return tcu::TestStatus::fail("vkQueueBindSparse didn't signal the fence");
293
294			if (!waitForFences(vk, getDevice(), regularFences))
295				return tcu::TestStatus::fail("Some fences weren't signaled (vkQueueBindSparse didn't signal semaphores?)");
296		}
297
298		// May return an error if some waitSemaphores didn't get signaled
299		VK_CHECK(vk.deviceWaitIdle(getDevice()));
300
301		return tcu::TestStatus::pass("Pass");
302	}
303
304private:
305	const TestParams	m_params;
306};
307
308class SparseQueueBindTest : public TestCase
309{
310public:
311					SparseQueueBindTest	(tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
312		: TestCase	(testCtx, name)
313		, m_params	(params)
314	{
315		DE_ASSERT(params.numQueues > 0u);
316		DE_ASSERT(params.numQueues == 1u || m_params.numWaitSemaphores > 0u || m_params.numSignalSemaphores > 0u);	// without any semaphores, only sparse queue will be used
317	}
318
319	TestInstance*	createInstance		(Context& context) const
320	{
321		return new SparseQueueBindTestInstance(context, m_params);
322	}
323
324	virtual void	checkSupport		(Context& context) const
325	{
326		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING);
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	} cases[] =
340	{
341		// case name									// numQueues, numWaitSems, numSignalSems, emptySubmission, checkFence
342		{ "no_dependency",								{	1u,	0u,	0u,	false,	false,	}},
343		{ "no_dependency_fence",						{	1u,	0u,	0u,	false,	true,	}},
344
345		{ "single_queue_wait_one",						{	1u,	1u,	0u,	false,	true,	}},
346		{ "single_queue_wait_many",						{	1u,	3u,	0u,	false,	true,	}},
347		{ "single_queue_signal_one",					{	1u,	0u,	1u,	false,	true,	}},
348		{ "single_queue_signal_many",					{	1u,	0u,	3u,	false,	true,	}},
349		{ "single_queue_wait_one_signal_one",			{	1u,	1u,	1u,	false,	true,	}},
350		{ "single_queue_wait_many_signal_many",			{	1u,	2u,	3u,	false,	true,	}},
351
352		{ "multi_queue_wait_one",						{	2u,	1u,	0u,	false,	true,	}},
353		{ "multi_queue_wait_many",						{	2u,	2u,	0u,	false,	true,	}},
354		{ "multi_queue_signal_one",						{	2u,	0u,	1u,	false,	true,	}},
355		{ "multi_queue_signal_many",					{	2u,	0u,	2u,	false,	true,	}},
356		{ "multi_queue_wait_one_signal_one",			{	2u,	1u,	1u,	false,	true,	}},
357		{ "multi_queue_wait_many_signal_many",			{	2u,	2u,	2u,	false,	true,	}},
358		{ "multi_queue_wait_one_signal_one_other",		{	2u,	1u,	1u,	false,	true,	}},
359		{ "multi_queue_wait_many_signal_many_other",	{	3u,	2u,	2u,	false,	true,	}},
360
361		{ "empty",										{	1u,	0u,	0u,	true,	false,	}},
362		{ "empty_fence",								{	1u,	0u,	0u,	true,	true,	}},
363	};
364
365	for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
366		group->addChild(new SparseQueueBindTest(group->getTestContext(), cases[caseNdx].name, cases[caseNdx].params));
367}
368
369} // anonymous ns
370
371//! Sparse queue binding edge cases and synchronization with semaphores/fences.
372//! Actual binding and usage is tested by other test groups.
373tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx)
374{
375	return createTestGroup(testCtx, "queue_bind", populateTestGroup);
376}
377
378} // sparse
379} // vkt
380