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