| /*------------------------------------------------------------------------ |
| * 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" |
| |
| 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, compute::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(compute::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(compute::makePipelineLayout(vk, device, *descriptorSetLayout)); |
| const vk::Unique<vk::VkPipeline> pipeline(compute::makeComputePipeline(vk, device, *pipelineLayout, *shaderModule)); |
| |
| const vk::Unique<vk::VkCommandPool> cmdPool(compute::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, |
| compute::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; |
| |
| if (m_conditionalData.useSecondaryBuffer) |
| { |
| const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT, |
| DE_NULL, |
| VK_TRUE // 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); |
| |
| if (m_conditionalData.useSecondaryBuffer) |
| { |
| recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer); |
| } |
| |
| de::SharedPtr<Draw::Buffer> conditionalBuffer; |
| if (m_conditionalData.conditionEnabled) |
| { |
| conditionalBuffer = createConditionalRenderingBuffer(m_context, m_conditionalData); |
| beginConditionalRendering(vk, *cmdBuffer, *conditionalBuffer, m_conditionalData); |
| } |
| |
| if (!m_conditionalData.useSecondaryBuffer) |
| { |
| recordDispatch(vk, *cmdBuffer, indirectBuffer); |
| } |
| |
| if (m_conditionalData.useSecondaryBuffer) |
| { |
| vk.endCommandBuffer(*secondaryCmdBuffer); |
| |
| vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get()); |
| } |
| |
| if (m_conditionalData.conditionEnabled) |
| { |
| vk.cmdEndConditionalRenderingEXT(*cmdBuffer); |
| } |
| |
| 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 |