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 41using namespace vk; 42using de::MovePtr; 43 44namespace vkt 45{ 46namespace sparse 47{ 48namespace 49{ 50 51typedef de::SharedPtr<Unique<VkSemaphore> > SemaphoreSp; 52typedef de::SharedPtr<Unique<VkFence> > FenceSp; 53 54struct 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 63struct 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 76QueueSubmission 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 104QueueSubmission 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 134bool 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 144class SparseQueueBindTestInstance : public SparseResourcesBaseInstance 145{ 146public: 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 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(createSemaphore(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(createSemaphore(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(createFence(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(createFence(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 309private: 310 const TestParams m_params; 311}; 312 313class SparseQueueBindTest : public TestCase 314{ 315public: 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 324 TestInstance* createInstance (Context& context) const 325 { 326 return new SparseQueueBindTestInstance(context, m_params); 327 } 328 329private: 330 const TestParams m_params; 331}; 332 333void 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. 374tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx) 375{ 376 return createTestGroup(testCtx, "queue_bind", "Queue bind sparse tests", populateTestGroup); 377} 378 379} // sparse 380} // vkt 381