blob: 6115d4555efd7f614e1d9d0b62a205cd975f2a71 [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 Synchronization primitive tests with single queue
*//*--------------------------------------------------------------------*/
#include "vktSynchronizationOperationSingleQueueTests.hpp"
#include "vkDefs.hpp"
#include "vktTestCase.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktTestGroupUtil.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkCmdUtil.hpp"
#include "deUniquePtr.hpp"
#include "tcuTestLog.hpp"
#include "vktSynchronizationUtil.hpp"
#include "vktSynchronizationOperation.hpp"
#include "vktSynchronizationOperationTestData.hpp"
#include "vktSynchronizationOperationResources.hpp"
namespace vkt
{
namespace synchronization
{
namespace
{
using namespace vk;
class BaseTestInstance : public TestInstance
{
public:
BaseTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
: TestInstance (context)
, m_opContext (context, pipelineCacheData)
, m_resource (new Resource(m_opContext, resourceDesc, writeOp.getResourceUsageFlags() | readOp.getResourceUsageFlags()))
, m_writeOp (writeOp.build(m_opContext, *m_resource))
, m_readOp (readOp.build(m_opContext, *m_resource))
{
}
protected:
OperationContext m_opContext;
const de::UniquePtr<Resource> m_resource;
const de::UniquePtr<Operation> m_writeOp;
const de::UniquePtr<Operation> m_readOp;
};
class EventTestInstance : public BaseTestInstance
{
public:
EventTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
: BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
{
}
tcu::TestStatus iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
const Unique<VkEvent> event (createEvent(vk, device));
const SyncInfo writeSync = m_writeOp->getSyncInfo();
const SyncInfo readSync = m_readOp->getSyncInfo();
beginCommandBuffer(vk, *cmdBuffer);
m_writeOp->recordCommands(*cmdBuffer);
vk.cmdSetEvent(*cmdBuffer, *event, writeSync.stageMask);
if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
{
const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 1u, &barrier, 0u, DE_NULL);
}
else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
{
const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier);
}
m_readOp->recordCommands(*cmdBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, queue, *cmdBuffer);
{
const Data expected = m_writeOp->getData();
const Data actual = m_readOp->getData();
if (0 != deMemCmp(expected.data, actual.data, expected.size))
return tcu::TestStatus::fail("Memory contents don't match");
}
return tcu::TestStatus::pass("OK");
}
};
class BarrierTestInstance : public BaseTestInstance
{
public:
BarrierTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
: BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
{
}
tcu::TestStatus iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
const Move<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
const SyncInfo writeSync = m_writeOp->getSyncInfo();
const SyncInfo readSync = m_readOp->getSyncInfo();
beginCommandBuffer(vk, *cmdBuffer);
m_writeOp->recordCommands(*cmdBuffer);
if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
{
const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
vk.cmdPipelineBarrier(*cmdBuffer, writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
}
else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
{
const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
vk.cmdPipelineBarrier(*cmdBuffer, writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
}
m_readOp->recordCommands(*cmdBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, queue, *cmdBuffer);
{
const Data expected = m_writeOp->getData();
const Data actual = m_readOp->getData();
if (0 != deMemCmp(expected.data, actual.data, expected.size))
return tcu::TestStatus::fail("Memory contents don't match");
}
return tcu::TestStatus::pass("OK");
}
};
class SemaphoreTestInstance : public BaseTestInstance
{
public:
SemaphoreTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
: BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
{
}
tcu::TestStatus iterate (void)
{
enum {WRITE=0, READ, COUNT};
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const Unique<VkSemaphore> semaphore (createSemaphore (vk, device));
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
const Move<VkCommandBuffer> ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
const VkPipelineStageFlags stageBits[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
const VkSubmitInfo submitInfo[2] =
{
{
VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // deUint32 waitSemaphoreCount;
DE_NULL, // const VkSemaphore* pWaitSemaphores;
(const VkPipelineStageFlags*)DE_NULL,
1u, // deUint32 commandBufferCount;
&cmdBuffers[WRITE], // const VkCommandBuffer* pCommandBuffers;
1u, // deUint32 signalSemaphoreCount;
&semaphore.get(), // const VkSemaphore* pSignalSemaphores;
},
{
VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
1u, // deUint32 waitSemaphoreCount;
&semaphore.get(), // const VkSemaphore* pWaitSemaphores;
stageBits, // const VkPipelineStageFlags* pWaitDstStageMask;
1u, // deUint32 commandBufferCount;
&cmdBuffers[READ], // const VkCommandBuffer* pCommandBuffers;
0u, // deUint32 signalSemaphoreCount;
DE_NULL, // const VkSemaphore* pSignalSemaphores;
}
};
const SyncInfo writeSync = m_writeOp->getSyncInfo();
const SyncInfo readSync = m_readOp->getSyncInfo();
beginCommandBuffer(vk, cmdBuffers[WRITE]);
m_writeOp->recordCommands(cmdBuffers[WRITE]);
if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
{
const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
}
else
{
const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
m_resource->getBuffer().handle, 0, VK_WHOLE_SIZE);
vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
}
endCommandBuffer(vk, cmdBuffers[WRITE]);
beginCommandBuffer(vk, cmdBuffers[READ]);
m_readOp->recordCommands(cmdBuffers[READ]);
endCommandBuffer(vk, cmdBuffers[READ]);
VK_CHECK(vk.queueSubmit(queue, 2u, submitInfo, DE_NULL));
VK_CHECK(vk.queueWaitIdle(queue));
{
const Data expected = m_writeOp->getData();
const Data actual = m_readOp->getData();
if (0 != deMemCmp(expected.data, actual.data, expected.size))
return tcu::TestStatus::fail("Memory contents don't match");
}
return tcu::TestStatus::pass("OK");
}
};
class FenceTestInstance : public BaseTestInstance
{
public:
FenceTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
: BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
{
}
tcu::TestStatus iterate (void)
{
enum {WRITE=0, READ, COUNT};
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
const Move<VkCommandBuffer> ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
const SyncInfo writeSync = m_writeOp->getSyncInfo();
const SyncInfo readSync = m_readOp->getSyncInfo();
beginCommandBuffer(vk, cmdBuffers[WRITE]);
m_writeOp->recordCommands(cmdBuffers[WRITE]);
if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
{
const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
}
endCommandBuffer(vk, cmdBuffers[WRITE]);
submitCommandsAndWait(vk, device, queue, cmdBuffers[WRITE]);
beginCommandBuffer(vk, cmdBuffers[READ]);
m_readOp->recordCommands(cmdBuffers[READ]);
endCommandBuffer(vk, cmdBuffers[READ]);
submitCommandsAndWait(vk, device, queue, cmdBuffers[READ]);
{
const Data expected = m_writeOp->getData();
const Data actual = m_readOp->getData();
if (0 != deMemCmp(expected.data, actual.data, expected.size))
return tcu::TestStatus::fail("Memory contents don't match");
}
return tcu::TestStatus::pass("OK");
}
};
class SyncTestCase : public TestCase
{
public:
SyncTestCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const SyncPrimitive syncPrimitive,
const ResourceDescription resourceDesc,
const OperationName writeOp,
const OperationName readOp,
PipelineCacheData& pipelineCacheData)
: TestCase (testCtx, name, description)
, m_resourceDesc (resourceDesc)
, m_writeOp (makeOperationSupport(writeOp, resourceDesc))
, m_readOp (makeOperationSupport(readOp, resourceDesc))
, m_syncPrimitive (syncPrimitive)
, m_pipelineCacheData (pipelineCacheData)
{
}
void initPrograms (SourceCollections& programCollection) const
{
m_writeOp->initPrograms(programCollection);
m_readOp->initPrograms(programCollection);
}
TestInstance* createInstance (Context& context) const
{
switch (m_syncPrimitive)
{
case SYNC_PRIMITIVE_FENCE:
return new FenceTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
case SYNC_PRIMITIVE_SEMAPHORE:
return new SemaphoreTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
case SYNC_PRIMITIVE_BARRIER:
return new BarrierTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
case SYNC_PRIMITIVE_EVENT:
return new EventTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
}
DE_ASSERT(0);
return DE_NULL;
}
private:
const ResourceDescription m_resourceDesc;
const de::UniquePtr<OperationSupport> m_writeOp;
const de::UniquePtr<OperationSupport> m_readOp;
const SyncPrimitive m_syncPrimitive;
PipelineCacheData& m_pipelineCacheData;
};
void createTests (tcu::TestCaseGroup* group, PipelineCacheData* pipelineCacheData)
{
tcu::TestContext& testCtx = group->getTestContext();
static const struct
{
const char* name;
SyncPrimitive syncPrimitive;
int numOptions;
} groups[] =
{
{ "fence", SYNC_PRIMITIVE_FENCE, 0, },
{ "semaphore", SYNC_PRIMITIVE_SEMAPHORE, 0, },
{ "barrier", SYNC_PRIMITIVE_BARRIER, 1, },
{ "event", SYNC_PRIMITIVE_EVENT, 1, },
};
for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx)
{
de::MovePtr<tcu::TestCaseGroup> synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name, ""));
for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx)
for (int readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx)
{
const OperationName writeOp = s_writeOps[writeOpNdx];
const OperationName readOp = s_readOps[readOpNdx];
const std::string opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp);
bool empty = true;
de::MovePtr<tcu::TestCaseGroup> opGroup (new tcu::TestCaseGroup(testCtx, opGroupName.c_str(), ""));
for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx)
{
const ResourceDescription& resource = s_resources[resourceNdx];
std::string name = getResourceName(resource);
if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource))
{
opGroup->addChild(new SyncTestCase(testCtx, name, "", groups[groupNdx].syncPrimitive, resource, writeOp, readOp, *pipelineCacheData));
empty = false;
}
}
if (!empty)
synchGroup->addChild(opGroup.release());
}
group->addChild(synchGroup.release());
}
}
} // anonymous
tcu::TestCaseGroup* createSynchronizedOperationSingleQueueTests (tcu::TestContext& testCtx, PipelineCacheData& pipelineCacheData)
{
return createTestGroup(testCtx, "single_queue", "Synchronization of a memory-modifying operation", createTests, &pipelineCacheData);
}
} // synchronization
} // vkt