blob: 1604e64010ffb02bd80dc5c7ce11b1136a2100c3 [file] [log] [blame]
/*------------------------------------------------------------------------
* 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
{
// a dummy 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