• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 using namespace vk;
42 using de::MovePtr;
43 
44 namespace vkt
45 {
46 namespace sparse
47 {
48 namespace
49 {
50 
51 typedef de::SharedPtr<Unique<VkSemaphore> >	SemaphoreSp;
52 typedef de::SharedPtr<Unique<VkFence> >		FenceSp;
53 
54 struct 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 
63 struct 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 
makeSubmissionRegular(const Queue * queue,const deUint32 numWaitSemaphores,const VkSemaphore * pWaitSemaphore,const VkPipelineStageFlags * pWaitDstStageMask,const deUint32 numSignalSemaphores,const VkSemaphore * pSignalSemaphore)76 QueueSubmission 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 
makeSubmissionSparse(const Queue * queue,const deUint32 numWaitSemaphores,const VkSemaphore * pWaitSemaphore,const deUint32 numSignalSemaphores,const VkSemaphore * pSignalSemaphore)104 QueueSubmission 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 
waitForFences(const DeviceInterface & vk,const VkDevice device,const std::vector<FenceSp> & fences)134 bool 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 
144 class SparseQueueBindTestInstance : public SparseResourcesBaseInstance
145 {
146 public:
SparseQueueBindTestInstance(Context & context,const TestParams & params)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 
iterate(void)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(makeSemaphore(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(makeSemaphore(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(makeFence(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(makeFence(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 
309 private:
310 	const TestParams	m_params;
311 };
312 
313 class SparseQueueBindTest : public TestCase
314 {
315 public:
SparseQueueBindTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)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 
createInstance(Context & context) const324 	TestInstance* createInstance (Context& context) const
325 	{
326 		return new SparseQueueBindTestInstance(context, m_params);
327 	}
328 
329 private:
330 	const TestParams	m_params;
331 };
332 
populateTestGroup(tcu::TestCaseGroup * group)333 void 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.
createQueueBindSparseTests(tcu::TestContext & testCtx)374 tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx)
375 {
376 	return createTestGroup(testCtx, "queue_bind", "Queue bind sparse tests", populateTestGroup);
377 }
378 
379 } // sparse
380 } // vkt
381