/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2021 The Khronos Group Inc. * Copyright (c) 2021 Valve Corporation. * * 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 Dynamic State tests mixing it with compute and transfer. *//*--------------------------------------------------------------------*/ #include "vktDynamicStateComputeTests.hpp" #include "vkBufferWithMemory.hpp" #include "vkObjUtil.hpp" #include "vkCmdUtil.hpp" #include "vkBarrierUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkTypeUtil.hpp" #include "tcuVector.hpp" #include #include #include #include #include #include #include #include namespace vkt { namespace DynamicState { namespace { using namespace vk; // Additional objects needed to set a given dynamic state that need to exist beyond the state-setting call. Empty by default. struct DynamicStateData { virtual ~DynamicStateData() {} }; // A vertex buffer and graphics pipeline are needed for vkCmdBindVertexBuffers2EXT(). struct BindVertexBuffersData : public DynamicStateData { private: using BufferPtr = de::MovePtr; using RenderPassPtr = Move; using LayoutPtr = Move; using ModulePtr = Move; using PipelinePtr = Move; static constexpr deUint32 kWidth = 16u; static constexpr deUint32 kHeight = 16u; VkExtent3D getExtent (void) { return makeExtent3D(kWidth, kHeight, 1u); } public: BindVertexBuffersData(Context& ctx) : m_vertexBuffer () , m_dataSize (0u) , m_vertexBufferSize (0ull) , m_renderPass () , m_pipelineLayout () , m_vertexShader () , m_graphicsPipeline () { const auto& vki = ctx.getInstanceInterface(); const auto phyDev = ctx.getPhysicalDevice(); const auto& vkd = ctx.getDeviceInterface(); const auto device = ctx.getDevice(); auto& alloc = ctx.getDefaultAllocator(); // Vertex buffer. tcu::Vec4 vertex (0.f, 0.f, 0.f, 1.f); m_dataSize = sizeof(vertex); m_vertexBufferSize = de::roundUp(static_cast(m_dataSize), getPhysicalDeviceProperties(vki, phyDev).limits.nonCoherentAtomSize); const auto bufferInfo = makeBufferCreateInfo(m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); m_vertexBuffer = BufferPtr(new BufferWithMemory(vkd, device, alloc, bufferInfo, MemoryRequirement::HostVisible)); auto& bufferAlloc = m_vertexBuffer->getAllocation(); deMemcpy(bufferAlloc.getHostPtr(), &vertex, m_dataSize); flushAlloc(vkd, device, bufferAlloc); // Empty render pass. m_renderPass = makeRenderPass(vkd, device); // Empty pipeline layout. m_pipelineLayout = makePipelineLayout(vkd, device); // Passthrough vertex shader. m_vertexShader = createShaderModule(vkd, device, ctx.getBinaryCollection().get("vert"), 0u); const auto extent = getExtent(); const std::vector viewports (1, makeViewport(extent)); const std::vector scissors (1, makeRect2D(extent)); const VkDynamicState state = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT; const VkPipelineDynamicStateCreateInfo dynamicStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineDynamicStateCreateFlags flags; 1u, // deUint32 dynamicStateCount; &state, // const VkDynamicState* pDynamicStates; }; // Graphics pipeline. m_graphicsPipeline = makeGraphicsPipeline(vkd, device, m_pipelineLayout.get(), m_vertexShader.get(), DE_NULL, DE_NULL, DE_NULL, DE_NULL, m_renderPass.get(), viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0u, 0u, nullptr, nullptr, nullptr, nullptr, nullptr, &dynamicStateInfo); } const BufferWithMemory* getVertexBuffer () const { return m_vertexBuffer.get(); } size_t getDataSize () const { return m_dataSize; } VkPipeline getPipeline () const { return m_graphicsPipeline.get(); } virtual ~BindVertexBuffersData() {} private: BufferPtr m_vertexBuffer; size_t m_dataSize; VkDeviceSize m_vertexBufferSize; RenderPassPtr m_renderPass; LayoutPtr m_pipelineLayout; ModulePtr m_vertexShader; PipelinePtr m_graphicsPipeline; }; // Function that records a state-setting command in the given command buffer. using RecordStateFunction = std::function; // State-setting functions void setViewport (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkViewport viewport = { 0.0f, // float x; 0.0f, // float y; 1.0f, // float width; 1.0f, // float height; 0.0f, // float minDepth; 1.0f, // float maxDepth; }; vkd->cmdSetViewport(cmdBuffer, 0u, 1u, &viewport); } void setScissor (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkRect2D scissor = { { 0, 0 }, // VkOffset2D offset; { 1u, 1u }, // VkExtent2D extent; }; vkd->cmdSetScissor(cmdBuffer, 0u, 1u, &scissor); } void setLineWidth (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetLineWidth(cmdBuffer, 1.0f); } void setDepthBias (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthBias(cmdBuffer, 0.0f, 0.0f, 0.0f); } void setBlendConstants (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const float blendConstants[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; vkd->cmdSetBlendConstants(cmdBuffer, blendConstants); } void setDepthBounds (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthBounds(cmdBuffer, 0.0f, 1.0f); } void setStencilCompareMask (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetStencilCompareMask(cmdBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, 0xFFu); } void setStencilWriteMask (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetStencilWriteMask(cmdBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, 0xFFu); } void setStencilReference (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetStencilReference(cmdBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, 0xFFu); } void setDiscardRectangle (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkRect2D rectangle = { { 0, 0 }, // VkOffset2D offset; { 1u, 1u }, // VkExtent2D extent; }; vkd->cmdSetDiscardRectangleEXT(cmdBuffer, 0u, 1u, &rectangle); } void setSampleLocations (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkSampleLocationEXT locations[] = { { 0.5f, 0.5f }, { 0.5f, 1.5f }, { 1.5f, 0.5f }, { 1.5f, 1.5f }, }; const VkSampleLocationsInfoEXT info = { VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, // VkStructureType sType; nullptr, // const void* pNext; VK_SAMPLE_COUNT_4_BIT, // VkSampleCountFlagBits sampleLocationsPerPixel; { 1u, 1u }, // VkExtent2D sampleLocationGridSize; 4u, // deUint32 sampleLocationsCount; locations, // const VkSampleLocationEXT* pSampleLocations; }; vkd->cmdSetSampleLocationsEXT(cmdBuffer, &info); } void setRTPipelineStatckSize (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetRayTracingPipelineStackSizeKHR(cmdBuffer, 4096u); } void setFragmentShadingRage (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkExtent2D fragmentSize = { 1u, 1u }; const VkFragmentShadingRateCombinerOpKHR combinerOps[2] = { VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, }; vkd->cmdSetFragmentShadingRateKHR(cmdBuffer, &fragmentSize, combinerOps); } void setLineStipple (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetLineStippleEXT(cmdBuffer, 1u, 1u); } void setCullMode (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetCullModeEXT(cmdBuffer, VK_CULL_MODE_FRONT_AND_BACK); } void setFrontFace (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetFrontFaceEXT(cmdBuffer, VK_FRONT_FACE_COUNTER_CLOCKWISE); } void setPrimitiveTopology (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetPrimitiveTopologyEXT(cmdBuffer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); } void setViewportWithCount (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkViewport viewport = { 0.0f, // float x; 0.0f, // float y; 1.0f, // float width; 1.0f, // float height; 0.0f, // float minDepth; 1.0f, // float maxDepth; }; vkd->cmdSetViewportWithCountEXT(cmdBuffer, 1u, &viewport); } void setScissorWithCount (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkRect2D scissor = { { 0, 0 }, // VkOffset2D offset; { 1u, 1u }, // VkExtent2D extent; }; vkd->cmdSetScissorWithCountEXT(cmdBuffer, 1u, &scissor); } void bindVertexBuffers (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData* data) { const auto bindData = dynamic_cast(data); DE_ASSERT(bindData != nullptr); const auto vertexBuffer = bindData->getVertexBuffer(); const auto dataSize = static_cast(bindData->getDataSize()); const auto bufferOffset = vertexBuffer->getAllocation().getOffset(); const auto stride = static_cast(0); const auto pipeline = bindData->getPipeline(); vkd->cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkd->cmdBindVertexBuffers2EXT(cmdBuffer, 0u, 1u, &vertexBuffer->get(), &bufferOffset, &dataSize, &stride); } void setDepthTestEnable (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthTestEnableEXT(cmdBuffer, VK_TRUE); } void setDepthWriteEnable (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthWriteEnableEXT(cmdBuffer, VK_TRUE); } void setDepthCompareOp (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthCompareOpEXT(cmdBuffer, VK_COMPARE_OP_LESS); } void setDepthBoundsTestEnable (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetDepthBoundsTestEnableEXT(cmdBuffer, VK_TRUE); } void setStencilTestEnable (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetStencilTestEnableEXT(cmdBuffer, VK_TRUE); } void setStencilOp (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { vkd->cmdSetStencilOpEXT(cmdBuffer, VK_STENCIL_FRONT_AND_BACK, VK_STENCIL_OP_ZERO, VK_STENCIL_OP_INCREMENT_AND_CLAMP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS); } void setViewportWScaling (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkViewportWScalingNV viewport = { 1.0f, // float xcoeff; 1.0f, // float ycoeff; }; vkd->cmdSetViewportWScalingNV(cmdBuffer, 0u, 1u, &viewport); } void setViewportShadingRatePalette (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkShadingRatePaletteEntryNV entry = VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV; const VkShadingRatePaletteNV palette = { 1u, // deUint32 shadingRatePaletteEntryCount; &entry, // const VkShadingRatePaletteEntryNV* pShadingRatePaletteEntries; }; vkd->cmdSetViewportShadingRatePaletteNV(cmdBuffer, 0u, 1u, &palette); } void setCoarseSamplingOrder (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkCoarseSampleLocationNV location = { 0u, // deUint32 pixelX; 0u, // deUint32 pixelY; 0u, // deUint32 sample; }; const VkCoarseSampleOrderCustomNV order = { VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV, // VkShadingRatePaletteEntryNV shadingRate; 1u, // deUint32 sampleCount; 1u, // deUint32 sampleLocationCount; &location // const VkCoarseSampleLocationNV* pSampleLocations; }; vkd->cmdSetCoarseSampleOrderNV(cmdBuffer, VK_COARSE_SAMPLE_ORDER_TYPE_CUSTOM_NV, 1u, &order); } void setExclusiveScissor (const DeviceInterface* vkd, VkCommandBuffer cmdBuffer, const DynamicStateData*) { const VkRect2D scissor = { { 0, 0 }, // VkOffset2D offset; { 1u, 1u }, // VkExtent2D extent; }; vkd->cmdSetExclusiveScissorNV(cmdBuffer, 0u, 1u, &scissor); } const VkDynamicState dynamicStateList[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_LINE_WIDTH, VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT, VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT, VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR, VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR, VK_DYNAMIC_STATE_LINE_STIPPLE_EXT, VK_DYNAMIC_STATE_CULL_MODE_EXT, VK_DYNAMIC_STATE_FRONT_FACE_EXT, VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT, VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT, VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_STENCIL_OP_EXT, VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV, VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV, VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV, VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV, }; // Information about a dynamic state. struct StateInfo { std::vector requirements; // List of required functionalities. RecordStateFunction recorder; // Function that records the state to the command buffer being used. }; // Returns the state info for a given dynamic state. const StateInfo& getDynamicStateInfo (VkDynamicState state) { // Maps a given state to its state info structure. using StateInfoMap = std::map; static const StateInfoMap result = { { VK_DYNAMIC_STATE_VIEWPORT, { {}, setViewport } }, { VK_DYNAMIC_STATE_SCISSOR, { {}, setScissor } }, { VK_DYNAMIC_STATE_LINE_WIDTH, { {}, setLineWidth } }, { VK_DYNAMIC_STATE_DEPTH_BIAS, { {}, setDepthBias } }, { VK_DYNAMIC_STATE_BLEND_CONSTANTS, { {}, setBlendConstants } }, { VK_DYNAMIC_STATE_DEPTH_BOUNDS, { {}, setDepthBounds } }, { VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, { {}, setStencilCompareMask } }, { VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, { {}, setStencilWriteMask } }, { VK_DYNAMIC_STATE_STENCIL_REFERENCE, { {}, setStencilReference } }, { VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT, { { "VK_EXT_discard_rectangles" }, setDiscardRectangle } }, { VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT, { { "VK_EXT_sample_locations" }, setSampleLocations } }, { VK_DYNAMIC_STATE_RAY_TRACING_PIPELINE_STACK_SIZE_KHR, { { "VK_KHR_ray_tracing_pipeline" }, setRTPipelineStatckSize } }, { VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR, { { "VK_KHR_fragment_shading_rate" }, setFragmentShadingRage } }, { VK_DYNAMIC_STATE_LINE_STIPPLE_EXT, { { "VK_EXT_line_rasterization" }, setLineStipple } }, { VK_DYNAMIC_STATE_CULL_MODE_EXT, { { "VK_EXT_extended_dynamic_state" }, setCullMode } }, { VK_DYNAMIC_STATE_FRONT_FACE_EXT, { { "VK_EXT_extended_dynamic_state" }, setFrontFace } }, { VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, { { "VK_EXT_extended_dynamic_state" }, setPrimitiveTopology } }, { VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT, { { "VK_EXT_extended_dynamic_state" }, setViewportWithCount } }, { VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT, { { "VK_EXT_extended_dynamic_state" }, setScissorWithCount } }, { VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT, { { "VK_EXT_extended_dynamic_state" }, bindVertexBuffers } }, { VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, { { "VK_EXT_extended_dynamic_state" }, setDepthTestEnable } }, { VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, { { "VK_EXT_extended_dynamic_state" }, setDepthWriteEnable } }, { VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, { { "VK_EXT_extended_dynamic_state" }, setDepthCompareOp } }, { VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, { { "VK_EXT_extended_dynamic_state" }, setDepthBoundsTestEnable } }, { VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, { { "VK_EXT_extended_dynamic_state" }, setStencilTestEnable } }, { VK_DYNAMIC_STATE_STENCIL_OP_EXT, { { "VK_EXT_extended_dynamic_state" }, setStencilOp } }, { VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV, { { "VK_NV_clip_space_w_scaling" }, setViewportWScaling } }, { VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV, { { "VK_NV_shading_rate_image"}, setViewportShadingRatePalette } }, { VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV, { { "VK_NV_shading_rate_image"}, setCoarseSamplingOrder } }, { VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV, { { "VK_NV_scissor_exclusive"}, setExclusiveScissor } }, }; const auto itr = result.find(state); DE_ASSERT(itr != result.end()); return itr->second; } // Returns the set of auxiliary data needed to set a given state. de::MovePtr getDynamicStateData (Context& ctx, VkDynamicState state) { // Create vertex buffer for VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT. if (state == VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT) return de::MovePtr(new BindVertexBuffersData(ctx)); // null pointer normally. return de::MovePtr(); } enum class OperType { COMPUTE = 0, TRANSFER }; enum class WhenToSet { BEFORE = 0, AFTER }; // Set dynamic state before or after attempting to run a compute or transfer operation. struct TestParams { OperType operationType; WhenToSet whenToSet; std::vector states; }; class DynamicStateComputeCase : public vkt::TestCase { public: DynamicStateComputeCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params); virtual ~DynamicStateComputeCase (void) {} virtual void checkSupport (Context& context) const; virtual void initPrograms (vk::SourceCollections& programCollection) const; virtual TestInstance* createInstance (Context& context) const; protected: TestParams m_params; }; class DynamicStateComputeInstance : public vkt::TestInstance { public: DynamicStateComputeInstance (Context& context, const TestParams& params); virtual ~DynamicStateComputeInstance (void) {} virtual tcu::TestStatus iterate (void); protected: tcu::TestStatus iterateTransfer (void); tcu::TestStatus iterateCompute (void); TestParams m_params; }; DynamicStateComputeCase::DynamicStateComputeCase(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) : vkt::TestCase (testCtx, name, description) , m_params (params) {} DynamicStateComputeInstance::DynamicStateComputeInstance (Context& context, const TestParams& params) : vkt::TestInstance (context) , m_params (params) {} void DynamicStateComputeCase::checkSupport (Context& context) const { // Check required functionalities. for (const auto& state : m_params.states) { const auto stateInfo = getDynamicStateInfo(state); for (const auto& functionality : stateInfo.requirements) context.requireDeviceFunctionality(functionality); } } void DynamicStateComputeCase::initPrograms (vk::SourceCollections& programCollection) const { if (m_params.operationType == OperType::COMPUTE) { std::ostringstream comp; comp << "#version 450\n" << "\n" << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" << "\n" << "layout (push_constant, std430) uniform PushConstants {\n" << " uint valueIndex;\n" << "} pc;\n" << "\n" << "layout (set=0, binding=0, std430) buffer OutputBlock {\n" << " uint value[];\n" << "} ob;\n" << "\n" << "void main ()\n" << "{\n" << " ob.value[pc.valueIndex] = 1u;\n" << "}\n" ; programCollection.glslSources.add("comp") << glu::ComputeSource(comp.str()); } if (de::contains(begin(m_params.states), end(m_params.states), VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT)) { // Passthrough vertex shader for stand-in graphics pipeline. std::ostringstream vert; vert << "#version 450\n" << "layout (location=0) in vec4 inVertex;\n" << "void main() {\n" << " gl_Position = inVertex;\n" << "}\n" ; programCollection.glslSources.add("vert") << glu::VertexSource(vert.str()); } } vkt::TestInstance* DynamicStateComputeCase::createInstance (Context& context) const { return new DynamicStateComputeInstance(context, m_params); } tcu::TestStatus DynamicStateComputeInstance::iterate (void) { if (m_params.operationType == OperType::COMPUTE) return iterateCompute(); else return iterateTransfer(); } void fillBuffer(const DeviceInterface& vkd, VkDevice device, BufferWithMemory& buffer, const std::vector &values) { auto& alloc = buffer.getAllocation(); deMemcpy(alloc.getHostPtr(), values.data(), de::dataSize(values)); flushAlloc(vkd, device, alloc); } tcu::TestStatus DynamicStateComputeInstance::iterateTransfer (void) { const auto& vki = m_context.getInstanceInterface(); const auto phyDev = m_context.getPhysicalDevice(); const auto& vkd = m_context.getDeviceInterface(); const auto device = m_context.getDevice(); auto& alloc = m_context.getDefaultAllocator(); const auto qIndex = m_context.getUniversalQueueFamilyIndex(); const auto queue = m_context.getUniversalQueue(); const auto cmdPool = makeCommandPool(vkd, device, qIndex); const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); // Prepare two host-visible buffers for a transfer operation, with one element per dynamic state. const deUint32 seqStart = 1611747605u; DE_ASSERT(!m_params.states.empty()); std::vector srcValues(m_params.states.size()); const decltype(srcValues) dstValues(srcValues.size(), 0u); std::iota(begin(srcValues), end(srcValues), seqStart); const auto elemSize = static_cast(sizeof(decltype(srcValues)::value_type)); const auto dataSize = static_cast(de::dataSize(srcValues)); const auto bufferSize = de::roundUp(dataSize, getPhysicalDeviceProperties(vki, phyDev).limits.nonCoherentAtomSize); const auto srcInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); const auto dstInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); BufferWithMemory srcBuffer (vkd, device, alloc, srcInfo, MemoryRequirement::HostVisible); BufferWithMemory dstBuffer (vkd, device, alloc, dstInfo, MemoryRequirement::HostVisible); // Fill source and destination buffer. fillBuffer(vkd, device, srcBuffer, srcValues); fillBuffer(vkd, device, dstBuffer, dstValues); beginCommandBuffer(vkd, cmdBuffer); // We need to preserve dynamic state data until the command buffer has run. std::vector> statesData; for (size_t stateIdx = 0; stateIdx < m_params.states.size(); ++stateIdx) { // Get extra data needed for using the dynamic state. const auto offset = elemSize * stateIdx; const auto& state = m_params.states[stateIdx]; const auto stateInfo = getDynamicStateInfo(state); statesData.push_back(getDynamicStateData(m_context, state)); // Record command if before. if (m_params.whenToSet == WhenToSet::BEFORE) stateInfo.recorder(&vkd, cmdBuffer, statesData.back().get()); // Transfer op (copy one buffer element per dynamic state). const VkBufferCopy region = { offset, offset, elemSize }; vkd.cmdCopyBuffer(cmdBuffer, srcBuffer.get(), dstBuffer.get(), 1u, ®ion); // Record command if after. if (m_params.whenToSet == WhenToSet::AFTER) stateInfo.recorder(&vkd, cmdBuffer, statesData.back().get()); } const auto barrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &barrier, 0u, nullptr, 0u, nullptr); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); // Invalidate alloc and check destination buffer. auto& dstBufferAlloc = dstBuffer.getAllocation(); invalidateAlloc(vkd, device, dstBufferAlloc); decltype(srcValues) results (srcValues.size()); deMemcpy(results.data(), dstBufferAlloc.getHostPtr(), de::dataSize(srcValues)); for (size_t valueIdx = 0; valueIdx < srcValues.size(); ++valueIdx) { const auto& orig = srcValues[valueIdx]; const auto& res = results[valueIdx]; if (orig != res) { std::ostringstream msg; msg << "Unexpected value found in destination buffer at position " << valueIdx << " (found=" << res << " expected=" << orig << ")"; TCU_FAIL(msg.str()); } } return tcu::TestStatus::pass("Pass"); } tcu::TestStatus DynamicStateComputeInstance::iterateCompute (void) { const auto& vki = m_context.getInstanceInterface(); const auto phyDev = m_context.getPhysicalDevice(); const auto& vkd = m_context.getDeviceInterface(); const auto device = m_context.getDevice(); auto& alloc = m_context.getDefaultAllocator(); const auto qIndex = m_context.getUniversalQueueFamilyIndex(); const auto queue = m_context.getUniversalQueue(); const auto cmdPool = makeCommandPool(vkd, device, qIndex); const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); const auto cmdBuffer = cmdBufferPtr.get(); DescriptorSetLayoutBuilder setLayoutBuilder; setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT); const auto setLayout = setLayoutBuilder.build(vkd, device); // Push constants. const deUint32 pcSize = static_cast(sizeof(deUint32)); const auto pcRange = makePushConstantRange(VK_SHADER_STAGE_COMPUTE_BIT, 0u, pcSize); // Pipeline. const VkPipelineLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineLayoutCreateFlags flags; 1u, // deUint32 setLayoutCount; &setLayout.get(), // const VkDescriptorSetLayout* pSetLayouts; 1u, // deUint32 pushConstantRangeCount; &pcRange, // const VkPushConstantRange* pPushConstantRanges; }; const auto pipelineLayout = createPipelineLayout(vkd, device, &layoutInfo); const auto shaderModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0u); const VkPipelineShaderStageCreateInfo shaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineShaderStageCreateFlags flags; VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage; shaderModule.get(), // VkShaderModule module; "main", // const char* pName; nullptr, // const VkSpecializationInfo* pSpecializationInfo; }; const VkComputePipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType; nullptr, // const void* pNext; 0u, // VkPipelineCreateFlags flags; shaderStageInfo, // VkPipelineShaderStageCreateInfo stage; pipelineLayout.get(), // VkPipelineLayout layout; DE_NULL, // VkPipeline basePipelineHandle; 0, // deInt32 basePipelineIndex; }; const auto pipeline = createComputePipeline(vkd, device, DE_NULL, &pipelineInfo); DE_ASSERT(!m_params.states.empty()); // Output buffer with one value per state. std::vector bufferData (m_params.states.size(), 0u); const auto dataSize (de::dataSize(bufferData)); const auto outputBufferSize = de::roundUp(static_cast(dataSize), getPhysicalDeviceProperties(vki, phyDev).limits.nonCoherentAtomSize); const auto bufferCreateInfo = makeBufferCreateInfo(outputBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); BufferWithMemory outputBuffer (vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible); auto& outputBufferAlloc = outputBuffer.getAllocation(); auto outputBufferPtr = outputBufferAlloc.getHostPtr(); deMemcpy(outputBufferPtr, bufferData.data(), dataSize); flushAlloc(vkd, device, outputBufferAlloc); // Descriptor set. DescriptorPoolBuilder poolBuilder; poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get()); const auto bufferInfo = makeDescriptorBufferInfo(outputBuffer.get(), 0ull, outputBufferSize); DescriptorSetUpdateBuilder updateBuilder; updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferInfo); updateBuilder.update(vkd, device); // Record and submit. beginCommandBuffer(vkd, cmdBuffer); // We need to preserve dynamic state data until the command buffer has run. std::vector> statesData; for (size_t stateIdx = 0; stateIdx < m_params.states.size(); ++stateIdx) { // Objects needed to set the dynamic state. const auto& state = m_params.states[stateIdx]; const auto stateInfo = getDynamicStateInfo(state); statesData.push_back(getDynamicStateData(m_context, state)); if (m_params.whenToSet == WhenToSet::BEFORE) stateInfo.recorder(&vkd, cmdBuffer, statesData.back().get()); vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get()); vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr); { // Each state will write to a different buffer position. const deUint32 pcData = static_cast(stateIdx); vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), VK_SHADER_STAGE_COMPUTE_BIT, 0u, pcSize, &pcData); } vkd.cmdDispatch(cmdBuffer, 1u, 1u, 1u); if (m_params.whenToSet == WhenToSet::AFTER) stateInfo.recorder(&vkd, cmdBuffer, statesData.back().get()); } // Barrier to read buffer contents. const auto barrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &barrier, 0u, nullptr, 0u, nullptr); endCommandBuffer(vkd, cmdBuffer); submitCommandsAndWait(vkd, device, queue, cmdBuffer); // Read and verify buffer contents. invalidateAlloc(vkd, device, outputBufferAlloc); deMemcpy(bufferData.data(), outputBufferPtr, dataSize); for (size_t idx = 0u; idx < bufferData.size(); ++idx) { if (bufferData[idx] != 1u) { std::ostringstream msg; msg << "Unexpected value found at buffer position " << idx << ": " << bufferData[idx]; TCU_FAIL(msg.str()); } } return tcu::TestStatus::pass("Pass"); } std::string getDynamicStateBriefName (VkDynamicState state) { const auto fullName = de::toString(state); const auto prefixLen = strlen("VK_DYNAMIC_STATE_"); return de::toLower(fullName.substr(prefixLen)); } } // anonymous tcu::TestCaseGroup* createDynamicStateComputeTests (tcu::TestContext& testCtx) { using GroupPtr = de::MovePtr; GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "compute_transfer", "Dynamic state mixed with compute and transfer operations")); const struct { OperType operationType; const char* name; } operations[] = { { OperType::COMPUTE, "compute" }, { OperType::TRANSFER, "transfer" }, }; const struct { WhenToSet when; const char* name; } moments[] = { { WhenToSet::BEFORE, "before" }, { WhenToSet::AFTER, "after" }, }; // Tests with a single dynamic state. { GroupPtr singleStateGroup(new tcu::TestCaseGroup(testCtx, "single", "Tests using a single dynamic state")); for (int operIdx = 0; operIdx < DE_LENGTH_OF_ARRAY(operations); ++operIdx) { GroupPtr operationGroup(new tcu::TestCaseGroup(testCtx, operations[operIdx].name, "")); for (int stateIdx = 0; stateIdx < DE_LENGTH_OF_ARRAY(dynamicStateList); ++stateIdx) { const auto state = dynamicStateList[stateIdx]; const auto stateName = getDynamicStateBriefName(state); GroupPtr stateGroup(new tcu::TestCaseGroup(testCtx, stateName.c_str(), "")); for (int momentIdx = 0; momentIdx < DE_LENGTH_OF_ARRAY(moments); ++momentIdx) { const TestParams testParams = { operations[operIdx].operationType, // OperType operationType; moments[momentIdx].when, // WhenToSet whenToSet; std::vector(1, state), // std::vector state; }; stateGroup->addChild(new DynamicStateComputeCase(testCtx, moments[momentIdx].name, "", testParams)); } operationGroup->addChild(stateGroup.release()); } singleStateGroup->addChild(operationGroup.release()); } mainGroup->addChild(singleStateGroup.release()); } // A few tests with several dynamic states. { GroupPtr multiStateGroup(new tcu::TestCaseGroup(testCtx, "multi", "Tests using multiple dynamic states")); for (int operIdx = 0; operIdx < DE_LENGTH_OF_ARRAY(operations); ++operIdx) { GroupPtr operationGroup(new tcu::TestCaseGroup(testCtx, operations[operIdx].name, "")); for (int momentIdx = 0; momentIdx < DE_LENGTH_OF_ARRAY(moments); ++momentIdx) { TestParams testParams = { operations[operIdx].operationType, // OperType operationType; moments[momentIdx].when, // WhenToSet whenToSet; std::vector(), // std::vector states; }; // Use the basic states so as not to introduce extra requirements. for (int stateIdx = 0; stateIdx < DE_LENGTH_OF_ARRAY(dynamicStateList); ++stateIdx) { testParams.states.push_back(dynamicStateList[stateIdx]); if (dynamicStateList[stateIdx] == VK_DYNAMIC_STATE_STENCIL_REFERENCE) break; } operationGroup->addChild(new DynamicStateComputeCase(testCtx, moments[momentIdx].name, "", testParams)); } multiStateGroup->addChild(operationGroup.release()); } mainGroup->addChild(multiStateGroup.release()); } return mainGroup.release(); } } // DynamicState } // vkt