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 &params)
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 &params)
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