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 uint32_t numQueues; //! use 2 or more to sync between different queues
57 uint32_t numWaitSemaphores;
58 uint32_t 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 uint32_t numWaitSemaphores,const VkSemaphore * pWaitSemaphore,const VkPipelineStageFlags * pWaitDstStageMask,const uint32_t numSignalSemaphores,const VkSemaphore * pSignalSemaphore)76 QueueSubmission makeSubmissionRegular(const Queue *queue, const uint32_t numWaitSemaphores,
77 const VkSemaphore *pWaitSemaphore, const VkPipelineStageFlags *pWaitDstStageMask,
78 const uint32_t numSignalSemaphores, const VkSemaphore *pSignalSemaphore)
79 {
80 const VkSubmitInfo submitInfo = {
81 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
82 DE_NULL, // const void* pNext;
83 numWaitSemaphores, // uint32_t waitSemaphoreCount;
84 pWaitSemaphore, // const VkSemaphore* pWaitSemaphores;
85 pWaitDstStageMask, // const VkPipelineStageFlags* pWaitDstStageMask;
86 0u, // uint32_t commandBufferCount;
87 DE_NULL, // const VkCommandBuffer* pCommandBuffers;
88 numSignalSemaphores, // uint32_t signalSemaphoreCount;
89 pSignalSemaphore, // const VkSemaphore* pSignalSemaphores;
90 };
91
92 QueueSubmission submission;
93 submission.isSparseBinding = false;
94 submission.queue = queue;
95 submission.info.regular = submitInfo;
96
97 return submission;
98 }
99
makeSubmissionSparse(const Queue * queue,const uint32_t numWaitSemaphores,const VkSemaphore * pWaitSemaphore,const uint32_t numSignalSemaphores,const VkSemaphore * pSignalSemaphore)100 QueueSubmission makeSubmissionSparse(const Queue *queue, const uint32_t numWaitSemaphores,
101 const VkSemaphore *pWaitSemaphore, const uint32_t numSignalSemaphores,
102 const VkSemaphore *pSignalSemaphore)
103 {
104 const VkBindSparseInfo bindInfo = {
105 VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, // VkStructureType sType;
106 DE_NULL, // const void* pNext;
107 numWaitSemaphores, // uint32_t waitSemaphoreCount;
108 pWaitSemaphore, // const VkSemaphore* pWaitSemaphores;
109 0u, // uint32_t bufferBindCount;
110 DE_NULL, // const VkSparseBufferMemoryBindInfo* pBufferBinds;
111 0u, // uint32_t imageOpaqueBindCount;
112 DE_NULL, // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds;
113 0u, // uint32_t imageBindCount;
114 DE_NULL, // const VkSparseImageMemoryBindInfo* pImageBinds;
115 numSignalSemaphores, // uint32_t signalSemaphoreCount;
116 pSignalSemaphore, // const VkSemaphore* pSignalSemaphores;
117 };
118
119 QueueSubmission submission;
120 submission.isSparseBinding = true;
121 submission.queue = queue;
122 submission.info.sparse = bindInfo;
123
124 return submission;
125 }
126
waitForFences(const DeviceInterface & vk,const VkDevice device,const std::vector<FenceSp> & fences)127 bool waitForFences(const DeviceInterface &vk, const VkDevice device, const std::vector<FenceSp> &fences)
128 {
129 for (std::vector<FenceSp>::const_iterator fenceSpIter = fences.begin(); fenceSpIter != fences.end(); ++fenceSpIter)
130 {
131 if (vk.waitForFences(device, 1u, &(***fenceSpIter), VK_TRUE, ~0ull) != VK_SUCCESS)
132 return false;
133 }
134 return true;
135 }
136
137 class SparseQueueBindTestInstance : public SparseResourcesBaseInstance
138 {
139 public:
SparseQueueBindTestInstance(Context & context,const TestParams & params)140 SparseQueueBindTestInstance(Context &context, const TestParams ¶ms)
141 : SparseResourcesBaseInstance(context)
142 , m_params(params)
143 {
144 DE_ASSERT(m_params.numQueues > 0u); // must use at least one queue
145 DE_ASSERT(!m_params.emptySubmission ||
146 (m_params.numWaitSemaphores == 0u &&
147 m_params.numSignalSemaphores == 0u)); // can't use semaphores if we don't submit
148 }
149
iterate(void)150 tcu::TestStatus iterate(void)
151 {
152 const Queue *sparseQueue = DE_NULL;
153 std::vector<const Queue *> otherQueues;
154
155 // Determine required queues and create a device that supports them
156 {
157 QueueRequirementsVec requirements;
158 requirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u));
159 requirements.push_back(QueueRequirements((VkQueueFlags)0, m_params.numQueues)); // any queue flags
160
161 createDeviceSupportingQueues(requirements);
162
163 sparseQueue = &getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0u);
164
165 // We probably have picked the sparse queue again, so filter it out
166 for (uint32_t queueNdx = 0u; queueNdx < m_params.numQueues; ++queueNdx)
167 {
168 const Queue *queue = &getQueue((VkQueueFlags)0, queueNdx);
169 if (queue->queueHandle != sparseQueue->queueHandle)
170 otherQueues.push_back(queue);
171 }
172 }
173
174 const DeviceInterface &vk = getDeviceInterface();
175
176 std::vector<SemaphoreSp> allSemaphores;
177 std::vector<VkSemaphore> waitSemaphores;
178 std::vector<VkSemaphore> signalSemaphores;
179 std::vector<VkPipelineStageFlags> signalSemaphoresWaitDstStageMask;
180 std::vector<QueueSubmission> queueSubmissions;
181
182 for (uint32_t i = 0; i < m_params.numWaitSemaphores; ++i)
183 {
184 allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
185 waitSemaphores.push_back(**allSemaphores.back());
186 }
187
188 for (uint32_t i = 0; i < m_params.numSignalSemaphores; ++i)
189 {
190 allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice())));
191 signalSemaphores.push_back(**allSemaphores.back());
192 signalSemaphoresWaitDstStageMask.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
193 }
194
195 // Prepare submissions: signal semaphores for the sparse bind operation
196 {
197 uint32_t numQueues = 1u + static_cast<uint32_t>(otherQueues.size());
198 uint32_t numSemaphores = m_params.numWaitSemaphores;
199
200 while (numSemaphores > 0u && numQueues > 0u)
201 {
202 if (numQueues == 1u) // sparse queue is assigned last
203 {
204 // sparse queue can handle regular submissions as well
205 queueSubmissions.push_back(makeSubmissionRegular(sparseQueue, 0u, DE_NULL, DE_NULL, numSemaphores,
206 getDataOrNullptr(waitSemaphores)));
207 numSemaphores = 0u;
208 numQueues = 0u;
209 }
210 else
211 {
212 queueSubmissions.push_back(
213 makeSubmissionRegular(otherQueues[numQueues - 2], 0u, DE_NULL, DE_NULL, 1u,
214 getDataOrNullptr(waitSemaphores, numSemaphores - 1)));
215 --numQueues;
216 --numSemaphores;
217 }
218 }
219 }
220
221 // Prepare submission: bind sparse
222 if (!m_params.emptySubmission)
223 {
224 queueSubmissions.push_back(
225 makeSubmissionSparse(sparseQueue, m_params.numWaitSemaphores, getDataOrNullptr(waitSemaphores),
226 m_params.numSignalSemaphores, getDataOrNullptr(signalSemaphores)));
227 }
228 else
229 {
230 // an unused submission, won't be used in a call to vkQueueBindSparse
231 queueSubmissions.push_back(makeSubmissionSparse(sparseQueue, 0u, DE_NULL, 0u, DE_NULL));
232 }
233
234 // Prepare submissions: wait on semaphores signaled by the sparse bind operation
235 if (!m_params.emptySubmission)
236 {
237 uint32_t numQueues = 1u + static_cast<uint32_t>(otherQueues.size());
238 uint32_t numSemaphores = m_params.numSignalSemaphores;
239
240 while (numSemaphores > 0u && numQueues > 0u)
241 {
242 if (numQueues == 1u)
243 {
244 queueSubmissions.push_back(
245 makeSubmissionRegular(sparseQueue, numSemaphores, getDataOrNullptr(signalSemaphores),
246 getDataOrNullptr(signalSemaphoresWaitDstStageMask), 0u, DE_NULL));
247 numSemaphores = 0u;
248 numQueues = 0u;
249 }
250 else
251 {
252 queueSubmissions.push_back(makeSubmissionRegular(
253 otherQueues[numQueues - 2], 1u, getDataOrNullptr(signalSemaphores, numSemaphores - 1),
254 getDataOrNullptr(signalSemaphoresWaitDstStageMask, numSemaphores - 1), 0u, DE_NULL));
255 --numQueues;
256 --numSemaphores;
257 }
258 }
259 }
260
261 // Submit to queues
262 {
263 std::vector<FenceSp> regularFences;
264 std::vector<FenceSp> bindSparseFences;
265
266 for (std::vector<QueueSubmission>::const_iterator submissionIter = queueSubmissions.begin();
267 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,
283 &submissionIter->info.sparse, fence));
284 }
285 else
286 {
287 regularFences.push_back(makeVkSharedPtr(createFence(vk, getDevice())));
288 VK_CHECK(vk.queueSubmit(submissionIter->queue->queueHandle, 1u, &submissionIter->info.regular,
289 **regularFences.back()));
290 }
291 }
292
293 if (!waitForFences(vk, getDevice(), bindSparseFences))
294 return tcu::TestStatus::fail("vkQueueBindSparse didn't signal the fence");
295
296 if (!waitForFences(vk, getDevice(), regularFences))
297 return tcu::TestStatus::fail(
298 "Some fences weren't signaled (vkQueueBindSparse didn't signal semaphores?)");
299 }
300
301 // May return an error if some waitSemaphores didn't get signaled
302 VK_CHECK(vk.deviceWaitIdle(getDevice()));
303
304 return tcu::TestStatus::pass("Pass");
305 }
306
307 private:
308 const TestParams m_params;
309 };
310
311 class SparseQueueBindTest : public TestCase
312 {
313 public:
SparseQueueBindTest(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)314 SparseQueueBindTest(tcu::TestContext &testCtx, const std::string &name, const TestParams ¶ms)
315 : TestCase(testCtx, name)
316 , m_params(params)
317 {
318 DE_ASSERT(params.numQueues > 0u);
319 DE_ASSERT(params.numQueues == 1u || m_params.numWaitSemaphores > 0u ||
320 m_params.numSignalSemaphores > 0u); // without any semaphores, only sparse queue will be used
321 }
322
createInstance(Context & context) const323 TestInstance *createInstance(Context &context) const
324 {
325 return new SparseQueueBindTestInstance(context, m_params);
326 }
327
checkSupport(Context & context) const328 virtual void checkSupport(Context &context) const
329 {
330 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING);
331 }
332
333 private:
334 const TestParams m_params;
335 };
336
populateTestGroup(tcu::TestCaseGroup * group)337 void populateTestGroup(tcu::TestCaseGroup *group)
338 {
339 const struct
340 {
341 std::string name;
342 TestParams params;
343 } cases[] = {
344 // case name // numQueues, numWaitSems, numSignalSems, emptySubmission, checkFence
345 {"no_dependency",
346 {
347 1u,
348 0u,
349 0u,
350 false,
351 false,
352 }},
353 {"no_dependency_fence",
354 {
355 1u,
356 0u,
357 0u,
358 false,
359 true,
360 }},
361
362 {"single_queue_wait_one",
363 {
364 1u,
365 1u,
366 0u,
367 false,
368 true,
369 }},
370 {"single_queue_wait_many",
371 {
372 1u,
373 3u,
374 0u,
375 false,
376 true,
377 }},
378 {"single_queue_signal_one",
379 {
380 1u,
381 0u,
382 1u,
383 false,
384 true,
385 }},
386 {"single_queue_signal_many",
387 {
388 1u,
389 0u,
390 3u,
391 false,
392 true,
393 }},
394 {"single_queue_wait_one_signal_one",
395 {
396 1u,
397 1u,
398 1u,
399 false,
400 true,
401 }},
402 {"single_queue_wait_many_signal_many",
403 {
404 1u,
405 2u,
406 3u,
407 false,
408 true,
409 }},
410
411 {"multi_queue_wait_one",
412 {
413 2u,
414 1u,
415 0u,
416 false,
417 true,
418 }},
419 {"multi_queue_wait_many",
420 {
421 2u,
422 2u,
423 0u,
424 false,
425 true,
426 }},
427 {"multi_queue_signal_one",
428 {
429 2u,
430 0u,
431 1u,
432 false,
433 true,
434 }},
435 {"multi_queue_signal_many",
436 {
437 2u,
438 0u,
439 2u,
440 false,
441 true,
442 }},
443 {"multi_queue_wait_one_signal_one",
444 {
445 2u,
446 1u,
447 1u,
448 false,
449 true,
450 }},
451 {"multi_queue_wait_many_signal_many",
452 {
453 2u,
454 2u,
455 2u,
456 false,
457 true,
458 }},
459 {"multi_queue_wait_one_signal_one_other",
460 {
461 2u,
462 1u,
463 1u,
464 false,
465 true,
466 }},
467 {"multi_queue_wait_many_signal_many_other",
468 {
469 3u,
470 2u,
471 2u,
472 false,
473 true,
474 }},
475
476 {"empty",
477 {
478 1u,
479 0u,
480 0u,
481 true,
482 false,
483 }},
484 {"empty_fence",
485 {
486 1u,
487 0u,
488 0u,
489 true,
490 true,
491 }},
492 };
493
494 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
495 group->addChild(new SparseQueueBindTest(group->getTestContext(), cases[caseNdx].name, cases[caseNdx].params));
496 }
497
498 } // namespace
499
500 //! Sparse queue binding edge cases and synchronization with semaphores/fences.
501 //! Actual binding and usage is tested by other test groups.
createQueueBindSparseTests(tcu::TestContext & testCtx)502 tcu::TestCaseGroup *createQueueBindSparseTests(tcu::TestContext &testCtx)
503 {
504 return createTestGroup(testCtx, "queue_bind", populateTestGroup);
505 }
506
507 } // namespace sparse
508 } // namespace vkt
509