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 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 304private: 305 const TestParams m_params; 306}; 307 308class SparseQueueBindTest : public TestCase 309{ 310public: 311 SparseQueueBindTest (tcu::TestContext& testCtx, const std::string& name, const TestParams& params) 312 : TestCase (testCtx, name) 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 319 TestInstance* createInstance (Context& context) const 320 { 321 return new SparseQueueBindTestInstance(context, m_params); 322 } 323 324 virtual void checkSupport (Context& context) const 325 { 326 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING); 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 } cases[] = 340 { 341 // case name // numQueues, numWaitSems, numSignalSems, emptySubmission, checkFence 342 { "no_dependency", { 1u, 0u, 0u, false, false, }}, 343 { "no_dependency_fence", { 1u, 0u, 0u, false, true, }}, 344 345 { "single_queue_wait_one", { 1u, 1u, 0u, false, true, }}, 346 { "single_queue_wait_many", { 1u, 3u, 0u, false, true, }}, 347 { "single_queue_signal_one", { 1u, 0u, 1u, false, true, }}, 348 { "single_queue_signal_many", { 1u, 0u, 3u, false, true, }}, 349 { "single_queue_wait_one_signal_one", { 1u, 1u, 1u, false, true, }}, 350 { "single_queue_wait_many_signal_many", { 1u, 2u, 3u, false, true, }}, 351 352 { "multi_queue_wait_one", { 2u, 1u, 0u, false, true, }}, 353 { "multi_queue_wait_many", { 2u, 2u, 0u, false, true, }}, 354 { "multi_queue_signal_one", { 2u, 0u, 1u, false, true, }}, 355 { "multi_queue_signal_many", { 2u, 0u, 2u, false, true, }}, 356 { "multi_queue_wait_one_signal_one", { 2u, 1u, 1u, false, true, }}, 357 { "multi_queue_wait_many_signal_many", { 2u, 2u, 2u, false, true, }}, 358 { "multi_queue_wait_one_signal_one_other", { 2u, 1u, 1u, false, true, }}, 359 { "multi_queue_wait_many_signal_many_other", { 3u, 2u, 2u, false, true, }}, 360 361 { "empty", { 1u, 0u, 0u, true, false, }}, 362 { "empty_fence", { 1u, 0u, 0u, true, true, }}, 363 }; 364 365 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 366 group->addChild(new SparseQueueBindTest(group->getTestContext(), cases[caseNdx].name, cases[caseNdx].params)); 367} 368 369} // anonymous ns 370 371//! Sparse queue binding edge cases and synchronization with semaphores/fences. 372//! Actual binding and usage is tested by other test groups. 373tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx) 374{ 375 return createTestGroup(testCtx, "queue_bind", populateTestGroup); 376} 377 378} // sparse 379} // vkt 380