| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Queue bind sparse tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktSparseResourcesQueueBindSparseTests.hpp" |
| #include "vktSparseResourcesTestsUtil.hpp" |
| #include "vktSparseResourcesBase.hpp" |
| #include "vktTestGroupUtil.hpp" |
| |
| #include "vkDefs.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deSharedPtr.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| using namespace vk; |
| using de::MovePtr; |
| |
| namespace vkt |
| { |
| namespace sparse |
| { |
| namespace |
| { |
| |
| typedef de::SharedPtr<Unique<VkSemaphore> > SemaphoreSp; |
| typedef de::SharedPtr<Unique<VkFence> > FenceSp; |
| |
| struct TestParams |
| { |
| deUint32 numQueues; //! use 2 or more to sync between different queues |
| deUint32 numWaitSemaphores; |
| deUint32 numSignalSemaphores; |
| bool emptySubmission; //! will make an empty bind sparse submission |
| bool bindSparseUseFence; |
| }; |
| |
| struct QueueSubmission |
| { |
| union InfoUnion |
| { |
| VkSubmitInfo regular; |
| VkBindSparseInfo sparse; |
| }; |
| |
| const Queue* queue; |
| bool isSparseBinding; |
| InfoUnion info; |
| }; |
| |
| QueueSubmission makeSubmissionRegular (const Queue* queue, |
| const deUint32 numWaitSemaphores, |
| const VkSemaphore* pWaitSemaphore, |
| const VkPipelineStageFlags* pWaitDstStageMask, |
| const deUint32 numSignalSemaphores, |
| const VkSemaphore* pSignalSemaphore) |
| { |
| const VkSubmitInfo submitInfo = |
| { |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| numWaitSemaphores, // uint32_t waitSemaphoreCount; |
| pWaitSemaphore, // const VkSemaphore* pWaitSemaphores; |
| pWaitDstStageMask, // const VkPipelineStageFlags* pWaitDstStageMask; |
| 0u, // uint32_t commandBufferCount; |
| DE_NULL, // const VkCommandBuffer* pCommandBuffers; |
| numSignalSemaphores, // uint32_t signalSemaphoreCount; |
| pSignalSemaphore, // const VkSemaphore* pSignalSemaphores; |
| }; |
| |
| QueueSubmission submission; |
| submission.isSparseBinding = false; |
| submission.queue = queue; |
| submission.info.regular = submitInfo; |
| |
| return submission; |
| } |
| |
| QueueSubmission makeSubmissionSparse (const Queue* queue, |
| const deUint32 numWaitSemaphores, |
| const VkSemaphore* pWaitSemaphore, |
| const deUint32 numSignalSemaphores, |
| const VkSemaphore* pSignalSemaphore) |
| { |
| const VkBindSparseInfo bindInfo = |
| { |
| VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| numWaitSemaphores, // uint32_t waitSemaphoreCount; |
| pWaitSemaphore, // const VkSemaphore* pWaitSemaphores; |
| 0u, // uint32_t bufferBindCount; |
| DE_NULL, // const VkSparseBufferMemoryBindInfo* pBufferBinds; |
| 0u, // uint32_t imageOpaqueBindCount; |
| DE_NULL, // const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; |
| 0u, // uint32_t imageBindCount; |
| DE_NULL, // const VkSparseImageMemoryBindInfo* pImageBinds; |
| numSignalSemaphores, // uint32_t signalSemaphoreCount; |
| pSignalSemaphore, // const VkSemaphore* pSignalSemaphores; |
| }; |
| |
| QueueSubmission submission; |
| submission.isSparseBinding = true; |
| submission.queue = queue; |
| submission.info.sparse = bindInfo; |
| |
| return submission; |
| } |
| |
| bool waitForFences (const DeviceInterface& vk, const VkDevice device, const std::vector<FenceSp>& fences) |
| { |
| for (std::vector<FenceSp>::const_iterator fenceSpIter = fences.begin(); fenceSpIter != fences.end(); ++fenceSpIter) |
| { |
| if (vk.waitForFences(device, 1u, &(***fenceSpIter), VK_TRUE, ~0ull) != VK_SUCCESS) |
| return false; |
| } |
| return true; |
| } |
| |
| class SparseQueueBindTestInstance : public SparseResourcesBaseInstance |
| { |
| public: |
| SparseQueueBindTestInstance (Context &context, const TestParams& params) |
| : SparseResourcesBaseInstance (context) |
| , m_params (params) |
| { |
| DE_ASSERT(m_params.numQueues > 0u); // must use at least one queue |
| DE_ASSERT(!m_params.emptySubmission || (m_params.numWaitSemaphores == 0u && m_params.numSignalSemaphores == 0u)); // can't use semaphores if we don't submit |
| } |
| |
| tcu::TestStatus iterate (void) |
| { |
| const Queue* sparseQueue = DE_NULL; |
| std::vector<const Queue*> otherQueues; |
| |
| // Determine required queues and create a device that supports them |
| { |
| QueueRequirementsVec requirements; |
| requirements.push_back(QueueRequirements(VK_QUEUE_SPARSE_BINDING_BIT, 1u)); |
| requirements.push_back(QueueRequirements((VkQueueFlags)0, m_params.numQueues)); // any queue flags |
| |
| createDeviceSupportingQueues(requirements); |
| |
| sparseQueue = &getQueue(VK_QUEUE_SPARSE_BINDING_BIT, 0u); |
| |
| // We probably have picked the sparse queue again, so filter it out |
| for (deUint32 queueNdx = 0u; queueNdx < m_params.numQueues; ++queueNdx) |
| { |
| const Queue* queue = &getQueue((VkQueueFlags)0, queueNdx); |
| if (queue->queueHandle != sparseQueue->queueHandle) |
| otherQueues.push_back(queue); |
| } |
| } |
| |
| const DeviceInterface& vk = getDeviceInterface(); |
| |
| std::vector<SemaphoreSp> allSemaphores; |
| std::vector<VkSemaphore> waitSemaphores; |
| std::vector<VkSemaphore> signalSemaphores; |
| std::vector<VkPipelineStageFlags> signalSemaphoresWaitDstStageMask; |
| std::vector<QueueSubmission> queueSubmissions; |
| |
| for (deUint32 i = 0; i < m_params.numWaitSemaphores; ++i) |
| { |
| allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice()))); |
| waitSemaphores.push_back(**allSemaphores.back()); |
| } |
| |
| for (deUint32 i = 0; i < m_params.numSignalSemaphores; ++i) |
| { |
| allSemaphores.push_back(makeVkSharedPtr(createSemaphore(vk, getDevice()))); |
| signalSemaphores.push_back(**allSemaphores.back()); |
| signalSemaphoresWaitDstStageMask.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); |
| } |
| |
| // Prepare submissions: signal semaphores for the sparse bind operation |
| { |
| deUint32 numQueues = 1u + static_cast<deUint32>(otherQueues.size()); |
| deUint32 numSemaphores = m_params.numWaitSemaphores; |
| |
| while (numSemaphores > 0u && numQueues > 0u) |
| { |
| if (numQueues == 1u) // sparse queue is assigned last |
| { |
| // sparse queue can handle regular submissions as well |
| queueSubmissions.push_back(makeSubmissionRegular( |
| sparseQueue, 0u, DE_NULL, DE_NULL, numSemaphores, getDataOrNullptr(waitSemaphores))); |
| numSemaphores = 0u; |
| numQueues = 0u; |
| } |
| else |
| { |
| queueSubmissions.push_back(makeSubmissionRegular( |
| otherQueues[numQueues - 2], 0u, DE_NULL, DE_NULL, 1u, getDataOrNullptr(waitSemaphores, numSemaphores - 1))); |
| --numQueues; |
| --numSemaphores; |
| } |
| } |
| } |
| |
| // Prepare submission: bind sparse |
| if (!m_params.emptySubmission) |
| { |
| queueSubmissions.push_back(makeSubmissionSparse( |
| sparseQueue, m_params.numWaitSemaphores, getDataOrNullptr(waitSemaphores), m_params.numSignalSemaphores, getDataOrNullptr(signalSemaphores))); |
| } |
| else |
| { |
| // an unused submission, won't be used in a call to vkQueueBindSparse |
| queueSubmissions.push_back(makeSubmissionSparse(sparseQueue, 0u, DE_NULL, 0u, DE_NULL)); |
| } |
| |
| // Prepare submissions: wait on semaphores signaled by the sparse bind operation |
| if (!m_params.emptySubmission) |
| { |
| deUint32 numQueues = 1u + static_cast<deUint32>(otherQueues.size()); |
| deUint32 numSemaphores = m_params.numSignalSemaphores; |
| |
| while (numSemaphores > 0u && numQueues > 0u) |
| { |
| if (numQueues == 1u) |
| { |
| queueSubmissions.push_back(makeSubmissionRegular( |
| sparseQueue, numSemaphores, getDataOrNullptr(signalSemaphores), getDataOrNullptr(signalSemaphoresWaitDstStageMask), 0u, DE_NULL)); |
| numSemaphores = 0u; |
| numQueues = 0u; |
| } |
| else |
| { |
| queueSubmissions.push_back(makeSubmissionRegular( |
| otherQueues[numQueues - 2], 1u, getDataOrNullptr(signalSemaphores, numSemaphores - 1), getDataOrNullptr(signalSemaphoresWaitDstStageMask, numSemaphores - 1), 0u, DE_NULL)); |
| --numQueues; |
| --numSemaphores; |
| } |
| } |
| } |
| |
| // Submit to queues |
| { |
| std::vector<FenceSp> regularFences; |
| std::vector<FenceSp> bindSparseFences; |
| |
| for (std::vector<QueueSubmission>::const_iterator submissionIter = queueSubmissions.begin(); submissionIter != queueSubmissions.end(); ++submissionIter) |
| { |
| if (submissionIter->isSparseBinding) |
| { |
| VkFence fence = DE_NULL; |
| |
| if (m_params.bindSparseUseFence) |
| { |
| bindSparseFences.push_back(makeVkSharedPtr(createFence(vk, getDevice()))); |
| fence = **bindSparseFences.back(); |
| } |
| |
| if (m_params.emptySubmission) |
| VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 0u, DE_NULL, fence)); |
| else |
| VK_CHECK(vk.queueBindSparse(submissionIter->queue->queueHandle, 1u, &submissionIter->info.sparse, fence)); |
| } |
| else |
| { |
| regularFences.push_back(makeVkSharedPtr(createFence(vk, getDevice()))); |
| VK_CHECK(vk.queueSubmit(submissionIter->queue->queueHandle, 1u, &submissionIter->info.regular, **regularFences.back())); |
| } |
| } |
| |
| if (!waitForFences(vk, getDevice(), bindSparseFences)) |
| return tcu::TestStatus::fail("vkQueueBindSparse didn't signal the fence"); |
| |
| if (!waitForFences(vk, getDevice(), regularFences)) |
| return tcu::TestStatus::fail("Some fences weren't signaled (vkQueueBindSparse didn't signal semaphores?)"); |
| } |
| |
| // May return an error if some waitSemaphores didn't get signaled |
| VK_CHECK(vk.deviceWaitIdle(getDevice())); |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| private: |
| const TestParams m_params; |
| }; |
| |
| class SparseQueueBindTest : public TestCase |
| { |
| public: |
| SparseQueueBindTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) |
| : TestCase (testCtx, name, description) |
| , m_params (params) |
| { |
| DE_ASSERT(params.numQueues > 0u); |
| DE_ASSERT(params.numQueues == 1u || m_params.numWaitSemaphores > 0u || m_params.numSignalSemaphores > 0u); // without any semaphores, only sparse queue will be used |
| } |
| |
| TestInstance* createInstance (Context& context) const |
| { |
| return new SparseQueueBindTestInstance(context, m_params); |
| } |
| |
| virtual void checkSupport (Context& context) const |
| { |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING); |
| } |
| |
| private: |
| const TestParams m_params; |
| }; |
| |
| void populateTestGroup(tcu::TestCaseGroup* group) |
| { |
| const struct |
| { |
| std::string name; |
| TestParams params; |
| std::string description; |
| } cases[] = |
| { |
| // case name // numQueues, numWaitSems, numSignalSems, emptySubmission, checkFence |
| { "no_dependency", { 1u, 0u, 0u, false, false, }, "submit without any semaphores", }, |
| { "no_dependency_fence", { 1u, 0u, 0u, false, true, }, "submit without any semaphores, signal a fence", }, |
| |
| { "single_queue_wait_one", { 1u, 1u, 0u, false, true, }, "only sparse queue, wait for semaphore(s)", }, |
| { "single_queue_wait_many", { 1u, 3u, 0u, false, true, }, "only sparse queue, wait for semaphore(s)", }, |
| { "single_queue_signal_one", { 1u, 0u, 1u, false, true, }, "only sparse queue, signal semaphore(s)", }, |
| { "single_queue_signal_many", { 1u, 0u, 3u, false, true, }, "only sparse queue, signal semaphore(s)", }, |
| { "single_queue_wait_one_signal_one", { 1u, 1u, 1u, false, true, }, "only sparse queue, wait for and signal semaphore(s)", }, |
| { "single_queue_wait_many_signal_many", { 1u, 2u, 3u, false, true, }, "only sparse queue, wait for and signal semaphore(s)", }, |
| |
| { "multi_queue_wait_one", { 2u, 1u, 0u, false, true, }, "sparse and other queues, wait for semaphore(s)", }, |
| { "multi_queue_wait_many", { 2u, 2u, 0u, false, true, }, "sparse and other queues, wait for semaphore(s)", }, |
| { "multi_queue_signal_one", { 2u, 0u, 1u, false, true, }, "sparse and other queues, signal semaphore(s)", }, |
| { "multi_queue_signal_many", { 2u, 0u, 2u, false, true, }, "sparse and other queues, signal semaphore(s)", }, |
| { "multi_queue_wait_one_signal_one", { 2u, 1u, 1u, false, true, }, "sparse and other queues, wait for and signal semaphore(s)", }, |
| { "multi_queue_wait_many_signal_many", { 2u, 2u, 2u, false, true, }, "sparse and other queues, wait for and signal semaphore(s)", }, |
| { "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", }, |
| { "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", }, |
| |
| { "empty", { 1u, 0u, 0u, true, false, }, "call vkQueueBindSparse with zero bindInfos", }, |
| { "empty_fence", { 1u, 0u, 0u, true, true, }, "call vkQueueBindSparse with zero bindInfos, signal a fence", }, |
| }; |
| |
| for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) |
| group->addChild(new SparseQueueBindTest(group->getTestContext(), cases[caseNdx].name, cases[caseNdx].description, cases[caseNdx].params)); |
| } |
| |
| } // anonymous ns |
| |
| //! Sparse queue binding edge cases and synchronization with semaphores/fences. |
| //! Actual binding and usage is tested by other test groups. |
| tcu::TestCaseGroup* createQueueBindSparseTests (tcu::TestContext& testCtx) |
| { |
| return createTestGroup(testCtx, "queue_bind", "Queue bind sparse tests", populateTestGroup); |
| } |
| |
| } // sparse |
| } // vkt |