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 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
304 private:
305 const TestParams m_params;
306 };
307
308 class SparseQueueBindTest : public TestCase
309 {
310 public:
SparseQueueBindTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)311 SparseQueueBindTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
312 : TestCase (testCtx, name, description)
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
createInstance(Context & context) const319 TestInstance* createInstance (Context& context) const
320 {
321 return new SparseQueueBindTestInstance(context, m_params);
322 }
323
checkSupport(Context & context) const324 virtual void checkSupport (Context& context) const
325 {
326 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING);
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