blob: bacc1e2565a1c072cdad0c68117d3fc30d86f48a [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2018 The Khronos Group Inc.
* Copyright (c) 2018 Danylo Piliaiev <danylo.piliaiev@gmail.com>
*
* 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 Test for conditional rendering of vkCmdDispatch* functions
*//*--------------------------------------------------------------------*/
#include "vktConditionalDispatchTests.hpp"
#include "vktConditionalRenderingTestUtil.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktComputeTestsUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuResource.hpp"
#include "vkDefs.hpp"
#include "vkCmdUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkObjUtil.hpp"
namespace vkt
{
namespace conditional
{
namespace
{
enum DispatchCommandType
{
DISPATCH_COMMAND_TYPE_DISPATCH = 0,
DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT,
DISPATCH_COMMAND_TYPE_DISPATCH_BASE,
DISPATCH_COMMAND_TYPE_DISPATCH_LAST
};
const char* getDispatchCommandTypeName (DispatchCommandType command)
{
switch (command)
{
case DISPATCH_COMMAND_TYPE_DISPATCH: return "dispatch";
case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT: return "dispatch_indirect";
case DISPATCH_COMMAND_TYPE_DISPATCH_BASE: return "dispatch_base";
default: DE_ASSERT(false);
}
return "";
}
struct ConditionalTestSpec
{
DispatchCommandType command;
int numCalls;
ConditionalData conditionalData;
};
class ConditionalDispatchTest : public vkt::TestCase
{
public:
ConditionalDispatchTest (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const ConditionalTestSpec& testSpec);
void initPrograms (vk::SourceCollections& sourceCollections) const;
TestInstance* createInstance (Context& context) const;
private:
const ConditionalTestSpec m_testSpec;
};
class ConditionalDispatchTestInstance : public TestInstance
{
public:
ConditionalDispatchTestInstance (Context &context, ConditionalTestSpec testSpec);
virtual tcu::TestStatus iterate (void);
void recordDispatch (const vk::DeviceInterface& vk,
vk::VkCommandBuffer cmdBuffer,
compute::Buffer& indirectBuffer);
protected:
const DispatchCommandType m_command;
const int m_numCalls;
const ConditionalData m_conditionalData;
};
ConditionalDispatchTest::ConditionalDispatchTest(tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const ConditionalTestSpec& testSpec)
: TestCase (testCtx, name, description)
, m_testSpec (testSpec)
{
}
void ConditionalDispatchTest::initPrograms (vk::SourceCollections& sourceCollections) const
{
std::ostringstream src;
src << "#version 310 es\n"
<< "layout(local_size_x = 1u, local_size_y = 1u, local_size_z = 1u) in;\n"
<< "layout(set = 0, binding = 0, std140) buffer Out\n"
<< "{\n"
<< " coherent uint count;\n"
<< "};\n"
<< "void main(void)\n"
<< "{\n"
<< " atomicAdd(count, 1u);\n"
<< "}\n";
sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
}
TestInstance* ConditionalDispatchTest::createInstance (Context& context) const
{
return new ConditionalDispatchTestInstance(context, m_testSpec);
}
ConditionalDispatchTestInstance::ConditionalDispatchTestInstance (Context &context, ConditionalTestSpec testSpec)
: TestInstance(context)
, m_command(testSpec.command)
, m_numCalls(testSpec.numCalls)
, m_conditionalData(testSpec.conditionalData)
{
checkConditionalRenderingCapabilities(context, m_conditionalData);
};
void ConditionalDispatchTestInstance::recordDispatch (const vk::DeviceInterface& vk,
vk::VkCommandBuffer cmdBuffer,
compute::Buffer& indirectBuffer)
{
for (int i = 0; i < m_numCalls; i++)
{
switch (m_command)
{
case DISPATCH_COMMAND_TYPE_DISPATCH:
{
vk.cmdDispatch(cmdBuffer, 1, 1, 1);
break;
}
case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT:
{
vk.cmdDispatchIndirect(cmdBuffer, *indirectBuffer, 0);
break;
}
case DISPATCH_COMMAND_TYPE_DISPATCH_BASE:
{
vk.cmdDispatchBase(cmdBuffer, 0, 0, 0, 1, 1, 1);
break;
}
default: DE_ASSERT(DE_FALSE);
}
}
}
tcu::TestStatus ConditionalDispatchTestInstance::iterate (void)
{
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
const vk::VkDevice device = m_context.getDevice();
const vk::VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
vk::Allocator& allocator = m_context.getDefaultAllocator();
// Create a buffer and host-visible memory for it
const vk::VkDeviceSize bufferSizeBytes = sizeof(deUint32);
const compute::Buffer outputBuffer(vk, device, allocator, vk::makeBufferCreateInfo(bufferSizeBytes, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), vk::MemoryRequirement::HostVisible);
{
const vk::Allocation& alloc = outputBuffer.getAllocation();
deUint8* outputBufferPtr = reinterpret_cast<deUint8*>(alloc.getHostPtr());
*(deUint32*)(outputBufferPtr) = 0;
vk::flushAlloc(vk, device, alloc);
}
// Create descriptor set
const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(
vk::DescriptorSetLayoutBuilder()
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
.build(vk, device));
const vk::Unique<vk::VkDescriptorPool> descriptorPool(
vk::DescriptorPoolBuilder()
.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
const vk::Unique<vk::VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
const vk::VkDescriptorBufferInfo descriptorInfo = vk::makeDescriptorBufferInfo(*outputBuffer, 0ull, bufferSizeBytes);
vk::DescriptorSetUpdateBuilder()
.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
.update(vk, device);
// Setup pipeline
const vk::Unique<vk::VkShaderModule> shaderModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
const vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
const vk::Unique<vk::VkPipeline> pipeline (compute::makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
const vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, queueFamilyIndex));
const vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
const vk::Unique<vk::VkCommandBuffer> secondaryCmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
// Create indirect buffer
const vk::VkDispatchIndirectCommand dispatchCommands[] = { { 1u, 1u, 1u } };
compute::Buffer indirectBuffer(
vk, device, allocator,
vk::makeBufferCreateInfo(sizeof(dispatchCommands), vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
vk::MemoryRequirement::HostVisible);
deUint8* indirectBufferPtr = reinterpret_cast<deUint8*>(indirectBuffer.getAllocation().getHostPtr());
deMemcpy(indirectBufferPtr, &dispatchCommands[0], sizeof(dispatchCommands));
vk::flushAlloc(vk, device, indirectBuffer.getAllocation());
// Start recording commands
beginCommandBuffer(vk, *cmdBuffer);
vk::VkCommandBuffer targetCmdBuffer = *cmdBuffer;
const bool useSecondaryCmdBuffer = m_conditionalData.conditionInherited || m_conditionalData.conditionInSecondaryCommandBuffer;
if (useSecondaryCmdBuffer)
{
const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT,
DE_NULL,
m_conditionalData.conditionInherited ? VK_TRUE : VK_FALSE // conditionalRenderingEnable
};
const vk::VkCommandBufferInheritanceInfo inheritanceInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
&conditionalRenderingInheritanceInfo,
0u, // renderPass
0u, // subpass
0u, // framebuffer
VK_FALSE, // occlusionQueryEnable
(vk::VkQueryControlFlags)0u, // queryFlags
(vk::VkQueryPipelineStatisticFlags)0u, // pipelineStatistics
};
const vk::VkCommandBufferBeginInfo commandBufferBeginInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
DE_NULL,
vk::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
&inheritanceInfo
};
vk.beginCommandBuffer(*secondaryCmdBuffer, &commandBufferBeginInfo);
targetCmdBuffer = *secondaryCmdBuffer;
}
vk.cmdBindPipeline(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
vk.cmdBindDescriptorSets(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
de::SharedPtr<Draw::Buffer> conditionalBuffer = createConditionalRenderingBuffer(m_context, m_conditionalData);
if (m_conditionalData.conditionInSecondaryCommandBuffer)
{
beginConditionalRendering(vk, *secondaryCmdBuffer, *conditionalBuffer, m_conditionalData);
recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
vk.cmdEndConditionalRenderingEXT(*secondaryCmdBuffer);
vk.endCommandBuffer(*secondaryCmdBuffer);
}
else if (m_conditionalData.conditionInherited)
{
recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
vk.endCommandBuffer(*secondaryCmdBuffer);
}
if (m_conditionalData.conditionInPrimaryCommandBuffer)
{
beginConditionalRendering(vk, *cmdBuffer, *conditionalBuffer, m_conditionalData);
if (m_conditionalData.conditionInherited)
{
vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
}
else
{
recordDispatch(vk, *cmdBuffer, indirectBuffer);
}
vk.cmdEndConditionalRenderingEXT(*cmdBuffer);
}
else if (useSecondaryCmdBuffer)
{
vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
}
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, queue, *cmdBuffer);
// Check result
qpTestResult res = QP_TEST_RESULT_PASS;
const vk::Allocation& outputBufferAllocation = outputBuffer.getAllocation();
invalidateAlloc(vk, device, outputBufferAllocation);
const deUint32* bufferPtr = static_cast<deUint32*>(outputBufferAllocation.getHostPtr());
const deUint32 expectedResult = m_conditionalData.expectCommandExecution ? m_numCalls : 0u;
if (bufferPtr[0] != expectedResult)
{
res = QP_TEST_RESULT_FAIL;
}
return tcu::TestStatus(res, qpGetTestResultName(res));
};
} // anonymous
ConditionalDispatchTests::ConditionalDispatchTests (tcu::TestContext &testCtx)
: TestCaseGroup (testCtx, "dispatch", "Conditional Rendering Of Dispatch Commands")
{
/* Left blank on purpose */
}
ConditionalDispatchTests::~ConditionalDispatchTests (void) {}
void ConditionalDispatchTests::init (void)
{
for (int conditionNdx = 0; conditionNdx < DE_LENGTH_OF_ARRAY(conditional::s_testsData); conditionNdx++)
{
const ConditionalData& conditionData = conditional::s_testsData[conditionNdx];
tcu::TestCaseGroup* conditionalDrawRootGroup = new tcu::TestCaseGroup(m_testCtx, de::toString(conditionData).c_str(), "Conditionaly execute dispatch calls");
for (deUint32 commandTypeIdx = 0; commandTypeIdx < DISPATCH_COMMAND_TYPE_DISPATCH_LAST; ++commandTypeIdx)
{
const DispatchCommandType command = DispatchCommandType(commandTypeIdx);
ConditionalTestSpec testSpec;
testSpec.command = command;
testSpec.numCalls = 3;
testSpec.conditionalData = conditionData;
conditionalDrawRootGroup->addChild(new ConditionalDispatchTest(m_testCtx, getDispatchCommandTypeName(command), "", testSpec));
}
addChild(conditionalDrawRootGroup);
}
}
} // conditional
} // vkt