From f84a385feac1ffebbb914181c43acb8c3a3073d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Feb 2021 20:13:30 +0100 Subject: [PATCH] Vk: initial rasterization pipeline creation. I named it RasterizationPipelineCreateInfo and not GraphicsPipelineCreateInfo because there's now a RayTracingPipelineCreateInfo as well, which is *also* graphics, and it would be confusing for everyone except people already drowned in Vulkan naming quirks. --- doc/snippets/MagnumVk.cpp | 22 + doc/vulkan-mapping.dox | 26 +- src/Magnum/Vk/CMakeLists.txt | 4 +- src/Magnum/Vk/DeviceFeatures.h | 15 +- .../dynamicRasterizationStateMapping.hpp | 49 ++ src/Magnum/Vk/MeshLayout.h | 5 +- src/Magnum/Vk/Pipeline.cpp | 277 ++++++++- src/Magnum/Vk/Pipeline.h | 105 +++- src/Magnum/Vk/PipelineLayout.h | 2 +- .../Vk/RasterizationPipelineCreateInfo.h | 534 ++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 5 +- src/Magnum/Vk/Test/PipelineTest.cpp | 462 ++++++++++++++- src/Magnum/Vk/Test/PipelineVkTest.cpp | 298 +++++++++- src/Magnum/Vk/Vk.h | 4 + 14 files changed, 1783 insertions(+), 25 deletions(-) create mode 100644 src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp create mode 100644 src/Magnum/Vk/RasterizationPipelineCreateInfo.h diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 558d7e203..666fdcbc9 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -52,8 +52,10 @@ #include "Magnum/Vk/MemoryAllocateInfo.h" #include "Magnum/Vk/MeshLayout.h" #include "Magnum/Vk/Pipeline.h" +#include "Magnum/Vk/PipelineLayout.h" #include "Magnum/Vk/PixelFormat.h" #include "Magnum/Vk/Queue.h" +#include "Magnum/Vk/RasterizationPipelineCreateInfo.h" #include "Magnum/Vk/RenderPassCreateInfo.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/ShaderCreateInfo.h" @@ -790,6 +792,26 @@ meshLayout /* [MeshLayout-usage] */ } +{ +Vk::Device device{NoCreate}; +/* The include should be a no-op here since it was already included above */ +/* [Pipeline-creation-rasterization] */ +#include + +DOXYGEN_IGNORE() + +Vk::ShaderSet shaderSet{DOXYGEN_IGNORE()}; +Vk::MeshLayout meshLayout{DOXYGEN_IGNORE(MeshPrimitive{})}; +Vk::PipelineLayout pipelineLayout{DOXYGEN_IGNORE(NoCreate)}; +Vk::RenderPass renderPass{DOXYGEN_IGNORE(NoCreate)}; + +Vk::Pipeline pipeline{device, Vk::RasterizationPipelineCreateInfo{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {800.0f, 600.0f}}) +}; +/* [Pipeline-creation-rasterization] */ +} + { Vk::Device device{NoCreate}; /* The include should be a no-op here since it was already included above */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index e2a2ae4ff..46b186491 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -70,7 +70,7 @@ Vulkan handle | Matching API @type_vk{ImageView} | @ref ImageView @type_vk{Instance} | @ref Instance @type_vk{PhysicalDevice} | @ref DeviceProperties -@type_vk{Pipeline} | | +@type_vk{Pipeline} | @ref Pipeline @type_vk{PipelineLayout} | @ref PipelineLayout @type_vk{QueryPool} | | @type_vk{Queue} | @ref Queue @@ -186,7 +186,6 @@ Vulkan function | Matching API @fn_vk{CreateBuffer}, \n @fn_vk{DestroyBuffer} | @ref Buffer constructor and destructor @fn_vk{CreateBufferView}, \n @fn_vk{DestroyBufferView} | | @fn_vk{CreateCommandPool}, \n @fn_vk{DestroyCommandPool} | @ref CommandPool constructor and destructor -@fn_vk{CreateComputePipelines}, \n @fn_vk{DestroyComputePipelines} | | @fn_vk{CreateDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CreateDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CreateDeferredOperationKHR} @m_class{m-label m-flat m-warning} **KHR**, \n @fn_vk{DestroyDeferredOperationKHR} @m_class{m-label m-flat m-warning} **KHR** | | @@ -200,11 +199,10 @@ Vulkan function | Matching API @fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | @ref Image constructor and destructor @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | @ref ImageView constructor and destructor @fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor -@fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | +@fn_vk{CreateGraphicsPipelines}, \n @fn_vk{CreateComputePipelines}, \n @fn_vk{CreateRayTracingPipelinesKHR} @m_class{m-label m-flat m-warning} **KHR**, \n @fn_vk{DestroyPipeline} | @ref Pipeline constructor and destructor @fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | | @fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | @ref PipelineLayout constructor and destructor @fn_vk{CreateQueryPool}, \n @fn_vk{DestroyQueryPool} | | -@fn_vk{CreateRayTracingPipelinesKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CreateRenderPass}, \n @fn_vk{CreateRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{DestroyRenderPass} | @ref RenderPass constructor and destructor @fn_vk{CreateSampler}, \n @fn_vk{DestroySampler} | | @fn_vk{CreateSamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** , \n @fn_vk{DestroySamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @@ -530,7 +528,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ -@type_vk{GraphicsPipelineCreateInfo} | | +@type_vk{GraphicsPipelineCreateInfo} | @ref RasterizationPipelineCreateInfo @subsection vulkan-mapping-structures-i I @@ -655,21 +653,21 @@ Vulkan structure | Matching API @type_vk{PhysicalDeviceVulkan12Properties} @m_class{m-label m-flat m-success} **1.2** | ignored for compatibility reasons @type_vk{PhysicalDeviceVulkanMemoryModelFeatures} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref DeviceFeatures @type_vk{PipelineCacheCreateInfo} | | -@type_vk{PipelineColorBlendAttachmentState} | | -@type_vk{PipelineColorBlendStateCreateInfo} | | -@type_vk{PipelineDepthStencilStateCreateInfo} | | -@type_vk{PipelineDynamicStateCreateInfo} | | +@type_vk{PipelineColorBlendAttachmentState} | @ref RasterizationPipelineCreateInfo +@type_vk{PipelineColorBlendStateCreateInfo} | @ref RasterizationPipelineCreateInfo +@type_vk{PipelineDepthStencilStateCreateInfo} | @ref RasterizationPipelineCreateInfo +@type_vk{PipelineDynamicStateCreateInfo} | @ref RasterizationPipelineCreateInfo @type_vk{PipelineInputAssemblyStateCreateInfo} | @ref MeshLayout @type_vk{PipelineLayoutCreateInfo} | @ref PipelineLayoutCreateInfo @type_vk{PipelineLibraryCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{PipelineMultisampleStateCreateInfo} | | -@type_vk{PipelineRasterizationStateCreateInfo} | | +@type_vk{PipelineMultisampleStateCreateInfo} | @ref RasterizationPipelineCreateInfo +@type_vk{PipelineRasterizationStateCreateInfo} | @ref RasterizationPipelineCreateInfo @type_vk{PipelineShaderStageCreateInfo} | @ref ShaderSet @type_vk{PipelineTessellationStateCreateInfo} | | @type_vk{PipelineTessellationDomainOriginStateCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{PipelineVertexInputDivisorStateCreateInfoEXT} @m_class{m-label m-flat m-warning} **EXT** | @ref MeshLayout @type_vk{PipelineVertexInputStateCreateInfo} | @ref MeshLayout -@type_vk{PipelineViewportStateCreateInfo} | | +@type_vk{PipelineViewportStateCreateInfo} | @ref RasterizationPipelineCreateInfo @type_vk{ProtectedSubmitInfo} | | @type_vk{PushConstantRange} | | @@ -831,7 +829,7 @@ Vulkan enum | Matching API @type_vk{DescriptorType} | | @type_vk{DeviceQueueCreateFlagBits}, \n @type_vk{DeviceQueueCreateFlags} | | @type_vk{DriverId} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{DynamicState} | | +@type_vk{DynamicState} | @ref DynamicRasterizationState @subsection vulkan-mapping-enums-e E @@ -921,7 +919,7 @@ Vulkan enum | Matching API @type_vk{PipelineCacheCreateFlagBits}, \n @type_vk{PipelineCacheCreateFlags} | | @type_vk{PipelineCacheHeaderVersion} | | @type_vk{PipelineCacheCreateFlagBits}, \n @type_vk{PipelineCacheCreateFlags} | | -@type_vk{PipelineCreateFlagBits}, \n @type_vk{PipelineCreateFlags} | | +@type_vk{PipelineCreateFlagBits}, \n @type_vk{PipelineCreateFlags} | @ref RasterizationPipelineCreateInfo::Flag, \n @ref RasterizationPipelineCreateInfo::Flags @type_vk{PipelineShaderStageCreateFlagBits}, \n @type_vk{PipelineShaderStageCreateFlags} | | @type_vk{PipelineStageFlagBits}, \n @type_vk{PipelineStageFlags} | @ref PipelineStage, \n @ref PipelineStages @type_vk{PointClippingBehavior} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 95b760323..a3e6dbe67 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -33,7 +33,6 @@ set(MagnumVk_SRCS Fence.cpp Framebuffer.cpp Handle.cpp - Pipeline.cpp PipelineLayout.cpp Queue.cpp Result.cpp @@ -58,6 +57,7 @@ set(MagnumVk_GracefulAssert_SRCS LayerProperties.cpp MeshLayout.cpp Memory.cpp + Pipeline.cpp PixelFormat.cpp RenderPass.cpp ShaderSet.cpp @@ -98,6 +98,7 @@ set(MagnumVk_HEADERS PipelineLayoutCreateInfo.h PixelFormat.h Queue.h + RasterizationPipelineCreateInfo.h RenderPass.h RenderPassCreateInfo.h Result.h @@ -121,6 +122,7 @@ set(MagnumVk_PRIVATE_HEADERS Implementation/compressedPixelFormatMapping.hpp Implementation/deviceFeatureMapping.hpp + Implementation/dynamicRasterizationStateMapping.hpp Implementation/pixelFormatMapping.hpp Implementation/structureHelpers.h Implementation/vertexFormatMapping.hpp) diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index df5d06d95..dcf958b10 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -1275,9 +1275,20 @@ enum class DeviceFeature: UnsignedShort { /* VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, #268 */ /** - * Extended dynamic state is supported. + * The @ref DynamicRasterizationState::CullMode, + * @relativeref{DynamicRasterizationState,FrontFace}, + * @relativeref{DynamicRasterizationState,PrimitiveTopology}, + * @relativeref{DynamicRasterizationState,ViewportWithCount}, + * @relativeref{DynamicRasterizationState,ScissorWithCount}, + * @relativeref{DynamicRasterizationState,VertexInputBindingStride}, + * @relativeref{DynamicRasterizationState,DepthTestEnable}, + * @relativeref{DynamicRasterizationState,DepthWriteEnable}, + * @relativeref{DynamicRasterizationState,DepthCompareOperation}, + * @relativeref{DynamicRasterizationState,DepthBoundsTestEnable}, + * @relativeref{DynamicRasterizationState,StencilTestEnable} and + * @relativeref{DynamicRasterizationState,StencilOperation} extended + * dynamic state is supported. * @requires_vk_extension Extension @vk_extension{EXT,extended_dynamic_state} - * @todoc list the actual DynamicState values once the enum exists */ ExtendedDynamicState, diff --git a/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp b/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp new file mode 100644 index 000000000..afd38311f --- /dev/null +++ b/src/Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp @@ -0,0 +1,49 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* See Magnum/Vk/RasterizationPipelineCreateInfo.cpp */ +#ifdef _c +_c(Viewport, VIEWPORT) +_c(Scissor, SCISSOR) +_c(LineWidth, LINE_WIDTH) +_c(DepthBias, DEPTH_BIAS) +_c(BlendConstants, BLEND_CONSTANTS) +_c(DepthBounds, DEPTH_BOUNDS) +_c(StencilCompareMask, STENCIL_COMPARE_MASK) +_c(StencilWriteMask, STENCIL_WRITE_MASK) +_c(StencilReference, STENCIL_REFERENCE) +_c(CullMode, CULL_MODE_EXT) +_c(FrontFace, FRONT_FACE_EXT) +_c(PrimitiveTopology, PRIMITIVE_TOPOLOGY_EXT) +_c(ViewportWithCount, VIEWPORT_WITH_COUNT_EXT) +_c(ScissorWithCount, SCISSOR_WITH_COUNT_EXT) +_c(VertexInputBindingStride, VERTEX_INPUT_BINDING_STRIDE_EXT) +_c(DepthTestEnable, DEPTH_TEST_ENABLE_EXT) +_c(DepthWriteEnable, DEPTH_WRITE_ENABLE_EXT) +_c(DepthCompareOperation, DEPTH_COMPARE_OP_EXT) +_c(DepthBoundsTestEnable, DEPTH_BOUNDS_TEST_ENABLE_EXT) +_c(StencilTestEnable, STENCIL_TEST_ENABLE_EXT) +_c(StencilOperation, STENCIL_OP_EXT) +#endif diff --git a/src/Magnum/Vk/MeshLayout.h b/src/Magnum/Vk/MeshLayout.h index ac6dfcede..86218823e 100644 --- a/src/Magnum/Vk/MeshLayout.h +++ b/src/Magnum/Vk/MeshLayout.h @@ -179,7 +179,8 @@ Wraps a @m_class{m-noindent} describing how vertex attributes are organized in buffers and what's the layout -of each attribute. +of each attribute. Used as an input for creating a +@ref Vk-Pipeline-creation-rasterization "rasterization pipeline". @section Vk-MeshLayout-usage Usage @@ -195,7 +196,7 @@ vertex buffer when drawing. @subsection Vk-MeshLayout-usage-comparison Layout comparison -Because a pipeline is tied to a particular mesh layout (apart from certain +Because a @ref Pipeline is tied to a particular mesh layout (apart from certain aspects that can be controlled via dynamic state), new pipelines should be created only when the layout is actually different. For that, the class provides a @ref operator==(), which returns @cpp true @ce when the two layouts diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index 52f18a037..ad1659971 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -24,15 +24,290 @@ */ #include "Pipeline.h" +#include "RasterizationPipelineCreateInfo.h" #include "CommandBuffer.h" -#include +#include +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Image.h" +#include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/MeshLayout.h" +#include "Magnum/Vk/ShaderSet.h" namespace Magnum { namespace Vk { +struct RasterizationPipelineCreateInfo::State { + Containers::Array colorBlendAttachments; + Containers::Array dynamicStates; + /** @todo make an array once we support multiview */ + VkViewport viewport; + VkRect2D scissor; +}; + +RasterizationPipelineCreateInfo::RasterizationPipelineCreateInfo(const ShaderSet& shaderSet, const MeshLayout& meshLayout, const VkPipelineLayout pipelineLayout, const VkRenderPass renderPass, const UnsignedInt subpass, const UnsignedInt subpassColorAttachmentCount, Flags flags): _info{}, _viewportInfo{}, _rasterizationInfo{}, _multisampleInfo{}, _depthStencilInfo{}, _colorBlendInfo{}, _dynamicInfo{}, _state{Containers::InPlaceInit} { + _info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + _info.flags = VkPipelineCreateFlags(flags); + _info.stageCount = shaderSet.stages().size(); + _info.pStages = shaderSet.stages(); + _info.pVertexInputState = meshLayout; + _info.pInputAssemblyState = meshLayout; + + /* pTesselationState is fine to be null */ + /** @todo add it if we get passed a tessellation shader? or should the + shader wrapper include it? */ + + /* Leaving pViewportState null as that gets (but doesn't have to, if + rasterization is disabled) set by setViewport() */ + + _rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + _rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL; + _rasterizationInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + _rasterizationInfo.lineWidth = 1.0f; + _info.pRasterizationState = &_rasterizationInfo; + + _multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + _multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + _info.pMultisampleState = &_multisampleInfo; + + _depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + _info.pDepthStencilState = &_depthStencilInfo; + + _state->colorBlendAttachments = Containers::Array{Containers::ValueInit, subpassColorAttachmentCount}; + for(VkPipelineColorBlendAttachmentState& i: _state->colorBlendAttachments) { + i.colorWriteMask = VK_COLOR_COMPONENT_R_BIT| + VK_COLOR_COMPONENT_G_BIT| + VK_COLOR_COMPONENT_B_BIT| + VK_COLOR_COMPONENT_A_BIT; + } + + _colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + _colorBlendInfo.attachmentCount = subpassColorAttachmentCount; + _colorBlendInfo.pAttachments = _state->colorBlendAttachments; + _info.pColorBlendState = &_colorBlendInfo; + + /* pDynamicState left null, gets set by setDynamicStates() if needed */ + + _info.layout = pipelineLayout; + _info.renderPass = renderPass; + _info.subpass = subpass; +} + +RasterizationPipelineCreateInfo::RasterizationPipelineCreateInfo(NoInitT) noexcept {} + +RasterizationPipelineCreateInfo::RasterizationPipelineCreateInfo(const VkGraphicsPipelineCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) +{ + /* Copy and reroute all top-level nested structs as well */ + if(info.pViewportState) + _info.pViewportState = &(_viewportInfo = *info.pViewportState); + if(info.pRasterizationState) + _info.pRasterizationState = &(_rasterizationInfo = *info.pRasterizationState); + if(info.pMultisampleState) + _info.pMultisampleState = &(_multisampleInfo = *info.pMultisampleState); + if(info.pDepthStencilState) + _info.pDepthStencilState = &(_depthStencilInfo = *info.pDepthStencilState); + if(info.pColorBlendState) + _info.pColorBlendState = &(_colorBlendInfo = *info.pColorBlendState); + if(info.pDynamicState) + _info.pDynamicState = &(_dynamicInfo = *info.pDynamicState); +} + +RasterizationPipelineCreateInfo::RasterizationPipelineCreateInfo(RasterizationPipelineCreateInfo&& other) noexcept: + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(other._info), + _viewportInfo(other._viewportInfo), + _rasterizationInfo(other._rasterizationInfo), + _multisampleInfo(other._multisampleInfo), + _depthStencilInfo(other._depthStencilInfo), + _colorBlendInfo(other._colorBlendInfo), + _dynamicInfo(other._dynamicInfo), + _state{std::move(other._state)} +{ + /* Reroute the pointers */ + if(_info.pViewportState == &other._viewportInfo) + _info.pViewportState = &_viewportInfo; + if(_info.pRasterizationState == &other._rasterizationInfo) + _info.pRasterizationState = &_rasterizationInfo; + if(_info.pMultisampleState == &other._multisampleInfo) + _info.pMultisampleState = &_multisampleInfo; + if(_info.pDepthStencilState == &other._depthStencilInfo) + _info.pDepthStencilState = &_depthStencilInfo; + if(_info.pColorBlendState == &other._colorBlendInfo) + _info.pColorBlendState = &_colorBlendInfo; + if(_info.pDynamicState == &other._dynamicInfo) + _info.pDynamicState = &_dynamicInfo; + + /* Ensure the previous instance doesn't reference state that's now ours */ + /** @todo this is now more like a destructible move, do it more selectively + and clear only what's really ours and not external? */ + other._info.pNext = nullptr; + other._info.stageCount = 0; + other._info.pStages = nullptr; + other._info.pVertexInputState = nullptr; + other._info.pInputAssemblyState = nullptr; + other._info.pTessellationState = nullptr; + other._info.pViewportState = nullptr; + other._info.pRasterizationState = nullptr; + other._info.pMultisampleState = nullptr; + other._info.pDepthStencilState = nullptr; + other._info.pColorBlendState = nullptr; + other._info.pDynamicState = nullptr; +} + +RasterizationPipelineCreateInfo::~RasterizationPipelineCreateInfo() = default; + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::operator=(RasterizationPipelineCreateInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._viewportInfo, _viewportInfo); + swap(other._rasterizationInfo, _rasterizationInfo); + swap(other._multisampleInfo, _multisampleInfo); + swap(other._depthStencilInfo, _depthStencilInfo); + swap(other._colorBlendInfo, _colorBlendInfo); + swap(other._dynamicInfo, _dynamicInfo); + swap(other._state, _state); + + /* Reroute the pointers */ + if(_info.pViewportState == &other._viewportInfo) + _info.pViewportState = &_viewportInfo; + if(_info.pRasterizationState == &other._rasterizationInfo) + _info.pRasterizationState = &_rasterizationInfo; + if(_info.pMultisampleState == &other._multisampleInfo) + _info.pMultisampleState = &_multisampleInfo; + if(_info.pDepthStencilState == &other._depthStencilInfo) + _info.pDepthStencilState = &_depthStencilInfo; + if(_info.pColorBlendState == &other._colorBlendInfo) + _info.pColorBlendState = &_colorBlendInfo; + if(_info.pDynamicState == &other._dynamicInfo) + _info.pDynamicState = &_dynamicInfo; + + /* And other way around as well */ + if(other._info.pViewportState == &_viewportInfo) + other._info.pViewportState = &other._viewportInfo; + if(other._info.pRasterizationState == &_rasterizationInfo) + other._info.pRasterizationState = &other._rasterizationInfo; + if(other._info.pMultisampleState == &_multisampleInfo) + other._info.pMultisampleState = &other._multisampleInfo; + if(other._info.pDepthStencilState == &_depthStencilInfo) + other._info.pDepthStencilState = &other._depthStencilInfo; + if(other._info.pColorBlendState == &_colorBlendInfo) + other._info.pColorBlendState = &other._colorBlendInfo; + if(other._info.pDynamicState == &_dynamicInfo) + other._info.pDynamicState = &other._dynamicInfo; + + return *this; +} + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setViewport(const Range3D& viewport, const Range2Di& scissor) { + _state->viewport = VkViewport(viewport); + _state->scissor = VkRect2D(scissor); + + _viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + _viewportInfo.viewportCount = 1; + _viewportInfo.pViewports = &_state->viewport; + _viewportInfo.scissorCount = 1; + _viewportInfo.pScissors = &_state->scissor; + _info.pViewportState = &_viewportInfo; + return *this; +} + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setViewport(const Range3D& viewport) { + return setViewport(viewport, Range2Di{viewport.xy()}); +} + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setViewport(const Range2D& viewport, const Range2Di& scissor) { + return setViewport(Range3D{{viewport.min(), 0.0f}, {viewport.max(), 1.0f}}, scissor); +} + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setViewport(const Range2D& viewport) { + return setViewport(viewport, Range2Di{viewport}); +} + +namespace { + +constexpr VkDynamicState DynamicRasterizationStateMapping[]{ + #define _c(value, vkValue) VK_DYNAMIC_STATE_ ## vkValue, + #include "Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp" + #undef _c +}; + +} + +RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setDynamicStates(const DynamicRasterizationStates& states) { + /* Count the number of states set, allocate for that */ + std::size_t count = 0; + for(std::size_t i = 0; i != DynamicRasterizationStates::Size; ++i) + count += Math::popcount(states.data()[i]); + _state->dynamicStates = Containers::Array{Containers::NoInit, count}; + + std::size_t offset = 0; + for(std::uint64_t i = 0; i != Containers::arraySize(DynamicRasterizationStateMapping); ++i) + if(states & DynamicRasterizationState(i)) + _state->dynamicStates[offset++] = DynamicRasterizationStateMapping[i]; + CORRADE_INTERNAL_ASSERT(offset == count); + + _dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + _dynamicInfo.dynamicStateCount = count; + _dynamicInfo.pDynamicStates = _state->dynamicStates; + _info.pDynamicState = &_dynamicInfo; + return *this; +} + +Pipeline Pipeline::wrap(Device& device, const VkPipeline handle, const HandleFlags flags) { + Pipeline out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): + _device{&device}, + #ifdef CORRADE_GRACEFUL_ASSERT + /* Otherwise vkDestroyPipeline() crashes when we hit the assert */ + _handle{}, + #endif + _flags{HandleFlag::DestroyOnDestruction} +{ + /* Doesn't check that the viewport is really a dynamic state, but should + catch most cases without false positives */ + CORRADE_ASSERT(info->pViewportState || info->pRasterizationState->rasterizerDiscardEnable || info->pDynamicState, + "Vk::Pipeline: if rasterization discard is not enabled, the viewport has to be either dynamic or set via setViewport()", ); + + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateGraphicsPipelines(device, {}, 1, info, nullptr, &_handle)); +} + +Pipeline::Pipeline(NoCreateT): _device{}, _handle{} {} + +Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Pipeline::~Pipeline() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyPipeline(*_device, _handle, nullptr); +} + +Pipeline& Pipeline::operator=(Pipeline&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkPipeline Pipeline::release() { + const VkPipeline handle = _handle; + _handle = {}; + return handle; +} + MemoryBarrier::MemoryBarrier(const Accesses sourceAccesses, const Accesses destinationAccesses): _barrier{} { _barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; _barrier.srcAccessMask = VkAccessFlags(sourceAccesses); diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 76603a03c..583e713c5 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::MemoryBarrier, @ref Magnum::Vk::BufferMemoryBarrier, @ref Magnum::Vk::ImageMemoryBarrier, enum @ref Magnum::Vk::PipelineStage, @ref Magnum::Vk::Access, @ref Magnum::Vk::DependencyFlag, enum set @ref Magnum::Vk::PipelineStages, @ref Magnum::Vk::Accesses, @ref Magnum::Vk::DependencyFlags + * @brief Class @ref Magnum::Vk::Pipeline, @ref Magnum::Vk::MemoryBarrier, @ref Magnum::Vk::BufferMemoryBarrier, @ref Magnum::Vk::ImageMemoryBarrier, enum @ref Magnum::Vk::PipelineStage, @ref Magnum::Vk::Access, @ref Magnum::Vk::DependencyFlag, enum set @ref Magnum::Vk::PipelineStages, @ref Magnum::Vk::Accesses, @ref Magnum::Vk::DependencyFlags * @m_since_latest */ @@ -40,6 +40,109 @@ namespace Magnum { namespace Vk { +/** +@brief Pipeline +@m_since_latest + +Wraps a @type_vk_keyword{Pipeline}. + +@section Vk-Pipeline-creation-rasterization Rasterization pipeline creation + +A @ref RasterizationPipelineCreateInfo is constructed from a @ref ShaderSet, +@ref MeshLayout, @ref PipelineLayout and a @ref RenderPass together with +subpass index and the count of color attachments. Apart from that you need to +set a viewport using @ref RasterizationPipelineCreateInfo::setViewport() +and you end up with a minimal setup needed for color-only rendering: + +@snippet MagnumVk.cpp Pipeline-creation-rasterization + +Certain aspects of the pipeline can be set as dynamic using +@relativeref{RasterizationPipelineCreateInfo,setDynamicStates()} --- in that +case a subset of the values passed to the constructor will be ignored. See the +particular @ref DynamicRasterizationState values for more information. +*/ +class MAGNUM_VK_EXPORT Pipeline { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the pipeline is created on + * @param handle The @type_vk{Pipeline} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a pipeline layout created using a constructor, the Vulkan pipeline + * layout is by default not deleted on destruction, use @p flags for + * different behavior. + * @see @ref release() + */ + static Pipeline wrap(Device& device, VkPipeline handle, HandleFlags flags = {}); + + /** + * @brief Construct a rasterization pipeline + * @param device Vulkan device to create the pipeline on + * @param info Rasterization pipeline creation info + * + * @see @fn_vk_keyword{CreateGraphicsPipelines} + */ + explicit Pipeline(Device& device, const RasterizationPipelineCreateInfo& info); + + /** + * @brief Construct without creating the pipeline layout + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Pipeline(NoCreateT); + + /** @brief Copying is not allowed */ + Pipeline(const Pipeline&) = delete; + + /** @brief Move constructor */ + Pipeline(Pipeline&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Pipeline} handle, unless the instance + * was created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyPipeline}, @ref release() + */ + ~Pipeline(); + + /** @brief Copying is not allowed */ + Pipeline& operator=(const Pipeline&) = delete; + + /** @brief Move assignment */ + Pipeline& operator=(Pipeline&& other) noexcept; + + /** @brief Underlying @type_vk{Pipeline} handle */ + VkPipeline handle() { return _handle; } + /** @overload */ + operator VkPipeline() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan pipeline + * + * Releases ownership of the Vulkan pipeline and returns its + * handle so @fn_vk{DestroyPipeline} is not called on destruction. The + * internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkPipeline release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkPipeline _handle; + HandleFlags _flags; +}; + /** @brief Pipeline stage @m_since_latest diff --git a/src/Magnum/Vk/PipelineLayout.h b/src/Magnum/Vk/PipelineLayout.h index ed3ba2788..2feec7f3d 100644 --- a/src/Magnum/Vk/PipelineLayout.h +++ b/src/Magnum/Vk/PipelineLayout.h @@ -43,7 +43,7 @@ namespace Magnum { namespace Vk { @brief Pipeline layout @m_since_latest -Wraps a @type_vk_keyword{PipelineLayout}. +Wraps a @type_vk_keyword{PipelineLayout}. Used in a @ref Pipeline. */ class MAGNUM_VK_EXPORT PipelineLayout { public: diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h new file mode 100644 index 000000000..834408c05 --- /dev/null +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -0,0 +1,534 @@ +#ifndef Magnum_Vk_RasterizationPipelineCreateInfo_h +#define Magnum_Vk_RasterizationPipelineCreateInfo_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::RasterizationPipelineCreateInfo, enum @ref Magnum::Vk::DynamicRasterizationState, enum set @ref Magnum::Vk::DynamicRasterizationStates + * @m_since_latest + */ + +#include +#include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Dynamic rasterization state +@m_since_latest + +Contains rasterization-related information from the +@type_vk_keyword{DynamicState} enum but with contiguos numbering to make it +usable as a set of @ref DynamicRasterizationStates. +@see @ref RasterizationPipelineCreateInfo::setDynamicStates() +*/ +enum class DynamicRasterizationState: UnsignedByte { + /** + * Viewport range set in + * @ref RasterizationPipelineCreateInfo::setViewport() is ignored and is + * expected to be set dynamically using @fn_vk{CmdSetViewport}. Viewport + * count is still set in @ref RasterizationPipelineCreateInfo, see + * @ref DynamicRasterizationState::ViewportWithCount for having both + * dynamic. + * @m_keywords{VK_DYNAMIC_STATE_VIEWPORT} + * @todoc link the multi-view API instead when exposed + */ + Viewport, + + /** + * Scissor rectangle set in + * @ref RasterizationPipelineCreateInfo::setViewport() is ignored and is + * expected to be set dynamically using @fn_vk{CmdSetScissor}. Scissor + * count is still set in @ref RasterizationPipelineCreateInfo, see + * @ref DynamicRasterizationState::ScissorWithCount for having both + * dynamic. + * @m_keywords{VK_DYNAMIC_STATE_SCISSOR} + * @todoc link the multi-view API instead when exposed + */ + Scissor, + + /** + * Line width set in @ref RasterizationPipelineCreateInfo is ignored and is + * expected to be set dynamically using @fn_vk{CmdSetLineWidth}. + * @requires_vk_feature @ref DeviceFeature::WideLines + * @m_keywords{VK_DYNAMIC_STATE_LINE_WIDTH} + * @todoc link to the actual API when exposed + */ + LineWidth, + + /** + * Depth bias constant factor, depth bias clamp and depth bias slope factor + * set in @ref RasterizationPipelineCreateInfo are ignored and expected to + * be set dynamically using @fn_vk{CmdSetDepthBias}. + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_BIAS} + * @todoc link to the actual API when exposed + */ + DepthBias, + + /** + * Blend constants set in @ref RasterizationPipelineCreateInfo are ignored + * and expected to be set dynamically using @fn_vk{CmdSetBlendConstants}. + * @m_keywords{VK_DYNAMIC_STATE_BLEND_CONSTANTS} + * @todoc link to the actual API when exposed + */ + BlendConstants, + + /** + * Min and max depth bounds set in @ref RasterizationPipelineCreateInfo are + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetDepthBounds}. + * @see @ref DynamicRasterizationState::DepthBoundsTestEnable + * @requires_vk_feature @ref DeviceFeature::DepthBounds + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_BOUNDS} + * @todoc link to the actual API when exposed + */ + DepthBounds, + + /** + * Stencil compare mask set in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetStencilCompareMask}. + * @m_keywords{VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK} + * @todoc link to the actual API when exposed + */ + StencilCompareMask, + + /** + * Stencil write mask set in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetStencilWriteMask}. + * @m_keywords{VK_DYNAMIC_STATE_STENCIL_WRITE_MASK} + * @todoc link to the actual API when exposed + */ + StencilWriteMask, + + /** + * Stencil reference set in @ref RasterizationPipelineCreateInfo is ignored + * and expected to be set dynamically using @fn_vk{CmdSetStencilReference}. + * @m_keywords{VK_DYNAMIC_STATE_STENCIL_REFERENCE} + * @todoc link to the actual API when exposed + */ + StencilReference, + + /** + * Cull mode set in @ref RasterizationPipelineCreateInfo is ignored and + * expected to be set dynamically using @fn_vk{CmdSetCullModeEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_CULL_MODE_EXT} + * @todoc link to the actual API when exposed + */ + CullMode, + + /** + * Front face set in @ref RasterizationPipelineCreateInfo is ignored and + * expected to be set dynamically using @fn_vk{CmdSetFrontFaceEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_FRONT_FACE_EXT} + * @todoc link to the actual API when exposed + */ + FrontFace, + + /** + * Only the @ref MeshPrimitive topology class set in @ref MeshLayout and + * passed to @ref RasterizationPipelineCreateInfo is used and the specific + * topology order and adjacency is expected to be set dynamically using + * @fn_vk{CmdSetPrimitiveTopologyEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT} + */ + PrimitiveTopology, + + /** + * Both the number of viewports and their ranges set in + * @ref RasterizationPipelineCreateInfo::setViewport() are ignored and + * expected to be set dynamically using @fn_vk{CmdSetViewportWithCountEXT}. + * A superset of @ref DynamicRasterizationState::Viewport. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT} + * @todoc link the multi-view API instead when exposed + */ + ViewportWithCount, + + /** + * Both the number of scissors and their rectangles set in + * @ref RasterizationPipelineCreateInfo::setViewport() are ignored and + * expected to be set dynamically using @fn_vk{CmdSetScissorWithCountEXT}. + * A superset of @ref DynamicRasterizationState::Scissor. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT} + * @todoc link the multi-view API instead when exposed + */ + ScissorWithCount, + + /** + * Stride set in @ref MeshLayout::addBinding() and passed to + * @ref RasterizationPipelineCreateInfo is ignored and expected to be set + * dynamically using @fn_vk{CmdBindVertexBuffers2EXT} + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT} + * @todoc link to the actual API when exposed + */ + VertexInputBindingStride, + + /** + * Depth test enablement in @ref RasterizationPipelineCreateInfo is ignored + * and expected to be set dynamically using @fn_vk{CmdSetDepthTestEnableEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT} + * @todoc link to the actual API when exposed + */ + DepthTestEnable, + + /** + * Depth write enablement in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetDepthWriteEnableEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT} + * @todoc link to the actual API when exposed + */ + DepthWriteEnable, + + /** + * Depth compare operation in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetDepthCompareOpEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT} + * @todoc link to the actual API when exposed + */ + DepthCompareOperation, + + /** + * Depth bounds test enablement in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetDepthBoundsTestEnableEXT}. + * @requires_vk_feature @ref DeviceFeature::DepthBounds and + * @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT} + * @todoc link to the actual API when exposed + */ + DepthBoundsTestEnable, + + /** + * Stencil test enablement in @ref RasterizationPipelineCreateInfo is + * ignored and expected to be set dynamically using + * @fn_vk{CmdSetStencilTestEnableEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT} + * @todoc link to the actual API when exposed + */ + StencilTestEnable, + + /** + * Stencil operation in @ref RasterizationPipelineCreateInfo is ignored and + * expected to be set dynamically using @fn_vk{CmdSetStencilOpEXT}. + * @requires_vk_feature @ref DeviceFeature::ExtendedDynamicState + * @m_keywords{VK_DYNAMIC_STATE_STENCIL_OP_EXT} + * @todoc link to the actual API when exposed + */ + StencilOperation, + + /** @todo RayTracingPipelineStackSize, or should that go into a dedicated + enum for RT pipeline? */ +}; + +/** +@brief Dynamic rasterization states +@m_since_latest + +A set of rasterization-related information from the +@type_vk_keyword{DynamicState} enum. +@see @ref RasterizationPipelineCreateInfo::setDynamicStates() +*/ +typedef Containers::BigEnumSet DynamicRasterizationStates; + +CORRADE_ENUMSET_OPERATORS(DynamicRasterizationStates) + +/** +@brief Rasterization pipeline creation info +@m_since_latest + +@m_keywords{GraphicsPipelineCreateInfo} + +Wraps a @type_vk_keyword{GraphicsPipelineCreateInfo}, along with + +- @type_vk_keyword{PipelineViewportStateCreateInfo}, +- @type_vk_keyword{PipelineRasterizationStateCreateInfo}, +- @type_vk_keyword{PipelineMultisampleStateCreateInfo}, +- @type_vk_keyword{PipelineDepthStencilStateCreateInfo}, +- @type_vk_keyword{PipelineColorBlendStateCreateInfo} containing + @type_vk_keyword{PipelineColorBlendAttachmentState} and +- @type_vk_keyword{PipelineDynamicStateCreateInfo}. + +See @ref Vk-Pipeline-creation-rasterization "Rasterization pipeline creation" +for usage information. +*/ +class MAGNUM_VK_EXPORT RasterizationPipelineCreateInfo { + public: + /** + * @brief Rasterization pipeline creation flag + * + * Wraps the rasterization-related subset of + * @type_vk_keyword{PipelineCreateFlagBits}. + * @see @ref Flags, @ref RasterizationPipelineCreateInfo(const ShaderSet&, const MeshLayout&, VkPipelineLayout, VkRenderPass, UnsignedInt, UnsignedInt, Flags) + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** + * Create the pipeline without optimization. + * + * @m_class{m-note m-success} + * + * @par + * Setting this flag on single-use pipelines might help + * drivers pick a better tradeoff between CPU time spent + * optimizing the pipeline and GPU time spent executing it. + */ + DisableOptimization = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT, + + /** + * Allow derivatives to be subsequently created from this pipeline. + */ + AllowDerivatives = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT, + + /** Derivative of a pipeline created earlier. */ + Derivative = VK_PIPELINE_CREATE_DERIVATIVE_BIT + }; + + /** + * @brief Rasterization pipeline creation flags + * + * Type-safe wrapper for the rasterization-related subset of + * @type_vk_keyword{PipelineCreateFlags}. + * @see @ref RasterizationPipelineCreateInfo(const ShaderSet&, const MeshLayout&, VkPipelineLayout, VkRenderPass, UnsignedInt, UnsignedInt, Flags) + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param shaderSet Set of shaders to use for this pipeline + * @param meshLayout Mesh layout to use in this pipeline + * @param pipelineLayout A @ref PipelineLayout or a raw Vulkan + * pipeline layout handle + * @param renderPass A @ref RenderPass or a raw Vulkan render + * pass handle + * @param subpass Subpass index + * @param subpassColorAttachmentCount Number of color attachments the + * @p subpass uses + * @param flags Rasterization pipeline creation flags + * + * Note that the @p shaderSet and @p meshLayout structure internals are + * referenced, not copied, and thus have to stay in scope until the + * @ref Pipeline object is created. + * + * The following @type_vk{GraphicsPipelineCreateInfo} and substructure + * fields are pre-filled in addition to `sType` of all referenced + * structures, everything else is zero-filled: + * + * - `flags` + * - `stageCount` and `pStages` to @p shaderSet + * - `pVertexInputState` and `pInputAssemblyState` to @p meshLayout + * - `pRasterizationState` + * - @cpp pRasterizationState->polygonMode @ce to + * @val_vk{POLYGON_MODE_FILL,PolygonMode} + * - @cpp pRasterizationState->frontFace @ce to + * @val_vk{FRONT_FACE_COUNTER_CLOCKWISE,FrontFace} + * - @cpp pRasterizationState->lineWidth @ce to @cpp 1.0f @ce + * - `pMultisampleState` + * - @cpp pMultisampleState->rasterizationSamples @ce to + * @val_vk{SAMPLE_COUNT_1_BIT,SampleCountFlagBits} + * - `pDepthStencilState` + * - `pColorBlendState` + * - @cpp pColorBlendState->attachmentCount @ce to + * @p subpassColorAttachmentCount + * - @cpp pColorBlendState->pAttachments @ce + * - @cpp pColorBlendState->pAttachments[i].colorWriteMask @ce to + * @val_vk{COLOR_COMPONENT_R_BIT,ColorComponentFlagBits}, + * @val_vk{COLOR_COMPONENT_G_BIT,ColorComponentFlagBits}, + * @val_vk{COLOR_COMPONENT_B_BIT,ColorComponentFlagBits} and + * @val_vk{COLOR_COMPONENT_A_BIT,ColorComponentFlagBits} + * - `layout` to @p pipelineLayout + * - `renderPass` + * - `subpass` + * + * You need to call at least @ref setViewport() or specifying + * @ref DynamicRasterizationState::Viewport in + * @ref setDynamicStates() for a valid setup. + */ + explicit RasterizationPipelineCreateInfo(const ShaderSet& shaderSet, const MeshLayout& meshLayout, VkPipelineLayout pipelineLayout, VkRenderPass renderPass, UnsignedInt subpass, UnsignedInt subpassColorAttachmentCount, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field nor the nested structure + * pointers are set --- the structure has to be fully initialized + * afterwards in order to be usable. + */ + explicit RasterizationPipelineCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values *including* the pointed-to + * + * - @type_vk{PipelineViewportStateCreateInfo}, + * - @type_vk{PipelineRasterizationStateCreateInfo}, + * - @type_vk{PipelineMultisampleStateCreateInfo}, + * - @type_vk{PipelineDepthStencilStateCreateInfo}, + * - @type_vk{PipelineColorBlendStateCreateInfo} and + * - @type_vk{PipelineDynamicStateCreateInfo} + * + * @m_class{m-noindent} + * + * structures verbatim, remaining pointers are kept unchanged without + * taking over the ownership. Modifying the newly created instance will + * not modify the original data nor the pointed-to data. + */ + explicit RasterizationPipelineCreateInfo(const VkGraphicsPipelineCreateInfo& info); + + /** @brief Copying is not allowed */ + RasterizationPipelineCreateInfo(const RasterizationPipelineCreateInfo&) = delete; + + /** @brief Move constructor */ + RasterizationPipelineCreateInfo(RasterizationPipelineCreateInfo&& other) noexcept; + + ~RasterizationPipelineCreateInfo(); + + /** @brief Copying is not allowed */ + RasterizationPipelineCreateInfo& operator=(const RasterizationPipelineCreateInfo&) = delete; + + /** @brief Move assignment */ + RasterizationPipelineCreateInfo& operator=(RasterizationPipelineCreateInfo&& other) noexcept; + + /** + * @brief Set viewport and scissor rectangle + * @param viewport Viewport rectangle and depth range + * @param scissor Scissor rectangle + * @return Reference to self (for method chaining) + * + * While the @p scissor rectangle is full pixels, the @p viewport XY + * range can have sub-pixel precision. The depth should be in range + * @f$ [0.0, 1.0] @f$. + * + * The following @type_vk{GraphicsPipelineCreateInfo} and substructure + * fields are modified, in addition to `sType` of newly referenced + * structures: + * + * - `pViewportState` + * - @cpp pViewportState->viewportCount @ce to @cpp 1 @ce + * - @cpp pViewportState->pViewports[0] @ce to @p viewport + * - @cpp pViewportState->scissorCount @ce to @cpp 1 @ce + * - @cpp pViewportState->pScissors[0] @ce to @p scissor + * + * @see @ref setViewport(const Range3D&) + * + * @requires_vk11 With the @vk_extension{KHR,maintenance1} extension + * it's possible to switch the origin from top left and Y down to + * bottom left and Y up (matching OpenGL) by supplying negative + * height and an adjusted Y offset + */ + RasterizationPipelineCreateInfo& setViewport(const Range3D& viewport, const Range2Di& scissor); + + /** + * @brief Set viewport with an implicit scissor rectangle + * + * Equivalent to calling @ref setViewport(const Range3D&, const Range2Di&) + * with @p scissor set to the @ref Range3D::xy() part of @p viewport. + */ + RasterizationPipelineCreateInfo& setViewport(const Range3D& viewport); + + /** + * @brief Set viewport with an implicit depth range + * + * Equivalent to calling @ref setViewport(const Range3D&, const Range2Di&) + * with depth from @cpp 0.0f @ce to @cpp 1.0f @ce. + */ + RasterizationPipelineCreateInfo& setViewport(const Range2D& viewport, const Range2Di& scissor); + + /** + * @brief Set viewport with an implicit depth range and scissor rectangle + * + * Equivalent to calling @ref setViewport(const Range3D&, const Range2Di&) + * with depth from @cpp 0.0f @ce to @cpp 1.0f @ce and @p scissor same + * as @p viewport. + */ + RasterizationPipelineCreateInfo& setViewport(const Range2D& viewport); + + /** + * @brief Set dynamic states + * + * The following @type_vk{GraphicsPipelineCreateInfo} and substructure + * fields are modified, in addition to `sType` of newly referenced + * structures: + * + * - `pDynamicState` + * - @cpp pDynamicState->dynamicStateCount @ce to count of values + * enabled in @p states + * - @cpp pDynamicState->pDynamicStates @ce to a list of + * @type_vk{DynamicState} corresponding to + * @ref DynamicRasterizationState values enabled in @p states + */ + RasterizationPipelineCreateInfo& setDynamicStates(const DynamicRasterizationStates& states); + + /** @brief Underlying @type_vk{GraphicsPipelineCreateInfo} structure */ + VkGraphicsPipelineCreateInfo& operator*() { return _info; } + /** @overload */ + const VkGraphicsPipelineCreateInfo& operator*() const { return _info; } + /** @overload */ + VkGraphicsPipelineCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkGraphicsPipelineCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkGraphicsPipelineCreateInfo*() const { return &_info; } + + private: + VkGraphicsPipelineCreateInfo _info; + VkPipelineViewportStateCreateInfo _viewportInfo; + VkPipelineRasterizationStateCreateInfo _rasterizationInfo; + VkPipelineMultisampleStateCreateInfo _multisampleInfo; + VkPipelineDepthStencilStateCreateInfo _depthStencilInfo; + VkPipelineColorBlendStateCreateInfo _colorBlendInfo; + VkPipelineDynamicStateCreateInfo _dynamicInfo; + + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(RasterizationPipelineCreateInfo::Flags) + +}} + +/* Make the definition complete -- it doesn't make sense to have a CreateInfo + without the corresponding object anyway. */ +#include "Magnum/Vk/Pipeline.h" + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 80544cfc1..eb45cf97d 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -176,7 +176,10 @@ if(BUILD_VK_TESTS) corrade_add_test(VkImageViewVkTest ImageViewVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) - corrade_add_test(VkPipelineVkTest PipelineVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + corrade_add_test(VkPipelineVkTest PipelineVkTest.cpp + LIBRARIES MagnumVkTestLib MagnumVulkanTester + FILES triangle-shaders.spv) + target_include_directories(VkPipelineVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(VkPipelineLayoutVkTest PipelineLayoutVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkQueueVkTest QueueVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkRenderPassVkTest RenderPassVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) diff --git a/src/Magnum/Vk/Test/PipelineTest.cpp b/src/Magnum/Vk/Test/PipelineTest.cpp index 7527a4e22..4933f7db3 100644 --- a/src/Magnum/Vk/Test/PipelineTest.cpp +++ b/src/Magnum/Vk/Test/PipelineTest.cpp @@ -24,18 +24,42 @@ */ #include +#include +#include #include +#include "Magnum/Math/Range.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Image.h" +#include "Magnum/Vk/MeshLayout.h" #include "Magnum/Vk/Pipeline.h" #include "Magnum/Vk/PixelFormat.h" +#include "Magnum/Vk/RasterizationPipelineCreateInfo.h" +#include "Magnum/Vk/ShaderSet.h" namespace Magnum { namespace Vk { namespace Test { namespace { struct PipelineTest: TestSuite::Tester { explicit PipelineTest(); + void dynamicRasterizationStateMapping(); + + void rasterizationCreateInfoConstruct(); + void rasterizationCreateInfoConstructNoInit(); + void rasterizationCreateInfoConstructFromVk(); + void rasterizationCreateInfoConstructCopy(); + void rasterizationCreateInfoConstructMove(); + void rasterizationCreateInfoConstructMoveExternalPointers(); + + void rasterizationCreateInfoViewportScissor(); + void rasterizationCreateInfoViewport2DScissor(); + void rasterizationCreateInfoViewportImplicitScissor(); + void rasterizationCreateInfoViewport2DImplicitScissor(); + void rasterizationCreateInfoDynamicState(); + + void constructNoCreate(); + void constructCopy(); + void memoryBarrierConstruct(); void memoryBarrierConstructNoInit(); void memoryBarrierConstructFromVk(); @@ -51,7 +75,25 @@ struct PipelineTest: TestSuite::Tester { }; PipelineTest::PipelineTest() { - addTests({&PipelineTest::memoryBarrierConstruct, + addTests({&PipelineTest::dynamicRasterizationStateMapping, + + &PipelineTest::rasterizationCreateInfoConstruct, + &PipelineTest::rasterizationCreateInfoConstructNoInit, + &PipelineTest::rasterizationCreateInfoConstructFromVk, + &PipelineTest::rasterizationCreateInfoConstructCopy, + &PipelineTest::rasterizationCreateInfoConstructMove, + &PipelineTest::rasterizationCreateInfoConstructMoveExternalPointers, + + &PipelineTest::rasterizationCreateInfoViewportScissor, + &PipelineTest::rasterizationCreateInfoViewport2DScissor, + &PipelineTest::rasterizationCreateInfoViewportImplicitScissor, + &PipelineTest::rasterizationCreateInfoViewport2DImplicitScissor, + &PipelineTest::rasterizationCreateInfoDynamicState, + + &PipelineTest::constructNoCreate, + &PipelineTest::constructCopy, + + &PipelineTest::memoryBarrierConstruct, &PipelineTest::memoryBarrierConstructNoInit, &PipelineTest::memoryBarrierConstructFromVk, @@ -65,6 +107,424 @@ PipelineTest::PipelineTest() { &PipelineTest::imageMemoryBarrierConstructFromVk}); } +void PipelineTest::dynamicRasterizationStateMapping() { + /* Same table is in Pipeline.cpp, here just to have something to test the + order with -- without this, correct order can't be verified */ + constexpr VkDynamicState mapping[]{ + #define _c(state, vkState) VK_DYNAMIC_STATE_ ## vkState, + #include "Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp" + #undef _c + }; + + /* This goes through all 8 bits of the enum range */ + UnsignedInt firstUnhandled = 0xff; + UnsignedInt nextHandled = 0; + for(UnsignedInt i = 0; i <= 0xff; ++i) { + const auto state = DynamicRasterizationState(i); + /* Each case verifies: + - that the entries are ordered by number by comparing a function to + expected result (so insertion here is done in proper place) + - that there was no gap (unhandled value inside the range) */ + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic error "-Wswitch" + #endif + switch(state) { + #define _c(state, vkState) \ + case DynamicRasterizationState::state: { \ + CORRADE_ITERATION(#state); \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xff); \ + CORRADE_COMPARE(mapping[i], VK_DYNAMIC_STATE_ ## vkState); \ + ++nextHandled; \ + continue; \ + } + #include "Magnum/Vk/Implementation/dynamicRasterizationStateMapping.hpp" + #undef _c + } + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif + + /* Not handled by any value, remember -- we might either be at the end + of the enum range (which is okay) or some value might be unhandled + here */ + firstUnhandled = i; + } + + CORRADE_COMPARE(firstUnhandled, 0xff); +} + +void PipelineTest::rasterizationCreateInfoConstruct() { + ShaderSet shaderSet; + shaderSet + .addShader({}, {}, {}) + .addShader({}, {}, {}); + + MeshLayout meshLayout{MeshPrimitive::Triangles}; + + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, reinterpret_cast(0xdead), reinterpret_cast(0xbeef), 15, 3, RasterizationPipelineCreateInfo::Flag::DisableOptimization|RasterizationPipelineCreateInfo::Flag::AllowDerivatives}; + CORRADE_COMPARE(info->flags, VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT|VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT); + CORRADE_COMPARE(info->stageCount, 2); + CORRADE_COMPARE(info->pStages, shaderSet.stages()); + CORRADE_COMPARE(info->pVertexInputState, &meshLayout.vkPipelineVertexInputStateCreateInfo()); + CORRADE_COMPARE(info->pInputAssemblyState, &meshLayout.vkPipelineInputAssemblyStateCreateInfo()); + CORRADE_VERIFY(!info->pViewportState); + CORRADE_VERIFY(info->pRasterizationState); + /* Not testing the default-filled pRasterizationState properties, feels as + excessive as verifying sType would */ + CORRADE_VERIFY(info->pMultisampleState); + /* Not testing the default-filled pMultisampleState nested properties, + feels as excessive as verifying sType would */ + CORRADE_VERIFY(info->pDepthStencilState); + /* Not testing the default-filled pDepthStencilState nested properties, + feels as excessive as verifying sType would */ + CORRADE_VERIFY(info->pColorBlendState); + CORRADE_COMPARE(info->pColorBlendState->attachmentCount, 3); + CORRADE_VERIFY(info->pColorBlendState->pAttachments); + for(std::size_t i = 0; i != 3; ++i) { + CORRADE_ITERATION(i); + CORRADE_COMPARE(info->pColorBlendState->pAttachments[i].colorWriteMask, VK_COLOR_COMPONENT_R_BIT|VK_COLOR_COMPONENT_G_BIT|VK_COLOR_COMPONENT_B_BIT|VK_COLOR_COMPONENT_A_BIT); + } + CORRADE_VERIFY(!info->pDynamicState); + CORRADE_COMPARE(info->layout, reinterpret_cast(0xdead)); + CORRADE_COMPARE(info->renderPass, reinterpret_cast(0xbeef)); + CORRADE_COMPARE(info->subpass, 15); +} + +void PipelineTest::rasterizationCreateInfoConstructNoInit() { + RasterizationPipelineCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) RasterizationPipelineCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void PipelineTest::rasterizationCreateInfoConstructFromVk() { + /* These keep referenced */ + int next{}; + VkPipelineShaderStageCreateInfo shaderSet{}; + VkPipelineVertexInputStateCreateInfo vertexInput{}; + VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; + VkPipelineTessellationStateCreateInfo tessellation{}; + + /* These get copied. Use deliberately wrong sType fields to check the + contents are copied verbatim. */ + VkPipelineViewportStateCreateInfo viewport{}; + viewport.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + VkPipelineRasterizationStateCreateInfo rasterization{}; + rasterization.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR; + VkPipelineMultisampleStateCreateInfo multisample{}; + multisample.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2; + VkPipelineDepthStencilStateCreateInfo depthStencil{}; + depthStencil.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + VkPipelineColorBlendStateCreateInfo colorBlend{}; + colorBlend.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; + VkPipelineDynamicStateCreateInfo dynamic{}; + dynamic.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; + VkGraphicsPipelineCreateInfo vkInfo{}; + vkInfo.sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO; + vkInfo.pNext = &next; + vkInfo.pStages = &shaderSet; + vkInfo.pVertexInputState = &vertexInput; + vkInfo.pInputAssemblyState = &inputAssembly; + vkInfo.pTessellationState = &tessellation; + vkInfo.pViewportState = &viewport; + vkInfo.pRasterizationState = &rasterization; + vkInfo.pMultisampleState = &multisample; + vkInfo.pDepthStencilState = &depthStencil; + vkInfo.pColorBlendState = &colorBlend; + vkInfo.pDynamicState = &dynamic; + + RasterizationPipelineCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO); + CORRADE_COMPARE(info->pNext, &next); + CORRADE_COMPARE(info->pStages, &shaderSet); + CORRADE_COMPARE(info->pVertexInputState, &vertexInput); + CORRADE_COMPARE(info->pInputAssemblyState, &inputAssembly); + CORRADE_COMPARE(info->pTessellationState, &tessellation); + CORRADE_VERIFY(info->pViewportState); + CORRADE_VERIFY(info->pViewportState != &viewport); + CORRADE_COMPARE(info->pViewportState->sType, VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2); + CORRADE_VERIFY(info->pRasterizationState); + CORRADE_VERIFY(info->pRasterizationState != &rasterization); + CORRADE_COMPARE(info->pRasterizationState->sType, VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR); + CORRADE_VERIFY(info->pMultisampleState); + CORRADE_VERIFY(info->pMultisampleState != &multisample); + CORRADE_COMPARE(info->pMultisampleState->sType, VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2); + CORRADE_VERIFY(info->pDepthStencilState); + CORRADE_VERIFY(info->pDepthStencilState != &depthStencil); + CORRADE_COMPARE(info->pDepthStencilState->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + CORRADE_VERIFY(info->pColorBlendState); + CORRADE_VERIFY(info->pColorBlendState != &colorBlend); + CORRADE_COMPARE(info->pColorBlendState->sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES); + CORRADE_VERIFY(info->pDynamicState); + CORRADE_VERIFY(info->pDynamicState != &dynamic); + CORRADE_COMPARE(info->pDynamicState->sType, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT); +} + +void PipelineTest::rasterizationCreateInfoConstructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +void PipelineTest::rasterizationCreateInfoConstructMove() { + ShaderSet shaderSet; + shaderSet + .addShader({}, {}, {}) + .addShader({}, {}, {}); + + MeshLayout meshLayout{MeshPrimitive::Triangles}; + + Containers::Pointer a{Containers::InPlaceInit, shaderSet, meshLayout, VkPipelineLayout{}, VkRenderPass{}, 0u, 3u}; + (*a).setViewport(Range3D{}) + .setDynamicStates(DynamicRasterizationState::CullMode); + + Containers::Pointer b{Containers::InPlaceInit, std::move(*a)}; + CORRADE_COMPARE((**a).stageCount, 0); + CORRADE_VERIFY(!(**a).pStages); + CORRADE_VERIFY(!(**a).pVertexInputState); + CORRADE_VERIFY(!(**a).pInputAssemblyState); + CORRADE_VERIFY(!(**a).pViewportState); + CORRADE_VERIFY(!(**a).pRasterizationState); + CORRADE_VERIFY(!(**a).pMultisampleState); + CORRADE_VERIFY(!(**a).pDepthStencilState); + CORRADE_VERIFY(!(**a).pColorBlendState); + CORRADE_VERIFY(!(**a).pDynamicState); + + /* Clear the original instance to verify we don't reference any of its + members */ + a.reset(); + CORRADE_COMPARE((**b).stageCount, 2); + CORRADE_COMPARE((**b).pStages, shaderSet.stages()); + CORRADE_COMPARE((**b).pVertexInputState, &meshLayout.vkPipelineVertexInputStateCreateInfo()); + CORRADE_COMPARE((**b).pInputAssemblyState, &meshLayout.vkPipelineInputAssemblyStateCreateInfo()); + CORRADE_VERIFY((**b).pViewportState); + CORRADE_COMPARE((**b).pViewportState->sType, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO); + CORRADE_VERIFY((**b).pRasterizationState); + CORRADE_COMPARE((**b).pRasterizationState->sType, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO); + CORRADE_VERIFY((**b).pMultisampleState); + CORRADE_COMPARE((**b).pMultisampleState->sType, VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO); + CORRADE_VERIFY((**b).pDepthStencilState); + CORRADE_COMPARE((**b).pDepthStencilState->sType, VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO); + CORRADE_VERIFY((**b).pColorBlendState); + CORRADE_COMPARE((**b).pColorBlendState->sType, VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO); + CORRADE_COMPARE((**b).pColorBlendState->attachmentCount, 3); + CORRADE_COMPARE((**b).pColorBlendState->pAttachments[2].colorWriteMask, VK_COLOR_COMPONENT_R_BIT|VK_COLOR_COMPONENT_G_BIT|VK_COLOR_COMPONENT_B_BIT|VK_COLOR_COMPONENT_A_BIT); + CORRADE_VERIFY((**b).pDynamicState); + CORRADE_COMPARE((**b).pDynamicState->sType, VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO); + + Containers::Pointer c{Containers::InPlaceInit, VkGraphicsPipelineCreateInfo{}}; + *c = std::move(*b); + CORRADE_COMPARE((**b).stageCount, 0); + CORRADE_VERIFY(!(**b).pStages); + CORRADE_VERIFY(!(**b).pVertexInputState); + CORRADE_VERIFY(!(**b).pInputAssemblyState); + CORRADE_VERIFY(!(**b).pViewportState); + CORRADE_VERIFY(!(**b).pRasterizationState); + CORRADE_VERIFY(!(**b).pMultisampleState); + CORRADE_VERIFY(!(**b).pDepthStencilState); + CORRADE_VERIFY(!(**b).pColorBlendState); + CORRADE_VERIFY(!(**b).pDynamicState); + + /* Clear the original instance to verify we don't reference any of its + members */ + b.reset(); + CORRADE_COMPARE((**c).stageCount, 2); + CORRADE_COMPARE((**c).pStages, shaderSet.stages()); + CORRADE_COMPARE((**c).pVertexInputState, &meshLayout.vkPipelineVertexInputStateCreateInfo()); + CORRADE_COMPARE((**c).pInputAssemblyState, &meshLayout.vkPipelineInputAssemblyStateCreateInfo()); + CORRADE_VERIFY((**c).pViewportState); + CORRADE_COMPARE((**c).pViewportState->sType, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO); + CORRADE_VERIFY((**c).pRasterizationState); + CORRADE_COMPARE((**c).pRasterizationState->sType, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO); + CORRADE_VERIFY((**c).pMultisampleState); + CORRADE_COMPARE((**c).pMultisampleState->sType, VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO); + CORRADE_VERIFY((**c).pDepthStencilState); + CORRADE_COMPARE((**c).pDepthStencilState->sType, VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO); + CORRADE_VERIFY((**c).pColorBlendState); + CORRADE_COMPARE((**c).pColorBlendState->sType, VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO); + CORRADE_COMPARE((**c).pColorBlendState->attachmentCount, 3); + CORRADE_COMPARE((**c).pColorBlendState->pAttachments[2].colorWriteMask, VK_COLOR_COMPONENT_R_BIT|VK_COLOR_COMPONENT_G_BIT|VK_COLOR_COMPONENT_B_BIT|VK_COLOR_COMPONENT_A_BIT); + CORRADE_VERIFY((**c).pDynamicState); + CORRADE_COMPARE((**c).pDynamicState->sType, VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void PipelineTest::rasterizationCreateInfoConstructMoveExternalPointers() { + VkPipelineViewportStateCreateInfo viewport{}; + VkPipelineRasterizationStateCreateInfo rasterization{}; + VkPipelineMultisampleStateCreateInfo multisample{}; + VkPipelineDepthStencilStateCreateInfo depthStencil{}; + VkPipelineColorBlendStateCreateInfo colorBlend{}; + VkPipelineDynamicStateCreateInfo dynamic{}; + + ShaderSet shaderSet; + shaderSet + .addShader({}, {}, {}) + .addShader({}, {}, {}); + + MeshLayout meshLayout{MeshPrimitive::Triangles}; + + /* Reroute the substructure pointers to external data */ + RasterizationPipelineCreateInfo a{shaderSet, meshLayout, {}, {}, 0, 0}; + a->pViewportState = &viewport; + a->pRasterizationState = &rasterization; + a->pMultisampleState = &multisample; + a->pDepthStencilState = &depthStencil; + a->pColorBlendState = &colorBlend; + a->pDynamicState = &dynamic; + + /* The external pointers should stay external, not rerouted to internal */ + RasterizationPipelineCreateInfo b = std::move(a); + CORRADE_COMPARE(b->pViewportState, &viewport); + CORRADE_COMPARE(b->pRasterizationState, &rasterization); + CORRADE_COMPARE(b->pMultisampleState, &multisample); + CORRADE_COMPARE(b->pDepthStencilState, &depthStencil); + CORRADE_COMPARE(b->pColorBlendState, &colorBlend); + CORRADE_COMPARE(b->pDynamicState, &dynamic); + + RasterizationPipelineCreateInfo c{VkGraphicsPipelineCreateInfo{}}; + c = std::move(b); + CORRADE_COMPARE(c->pViewportState, &viewport); + CORRADE_COMPARE(c->pRasterizationState, &rasterization); + CORRADE_COMPARE(c->pMultisampleState, &multisample); + CORRADE_COMPARE(c->pDepthStencilState, &depthStencil); + CORRADE_COMPARE(c->pColorBlendState, &colorBlend); + CORRADE_COMPARE(c->pDynamicState, &dynamic); +} + +void PipelineTest::rasterizationCreateInfoViewportScissor() { + ShaderSet shaderSet; + MeshLayout meshLayout{MeshPrimitive::Triangles}; + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, {}, {}, 0, 1}; + info.setViewport({{1.5f, 3.5f, 5.5f}, {8, 12, 14}}, {{15, 34}, {84, 72}}); + + CORRADE_VERIFY(info->pViewportState); + CORRADE_COMPARE(info->pViewportState->viewportCount, 1); + CORRADE_VERIFY(info->pViewportState->pViewports); + CORRADE_COMPARE(info->pViewportState->pViewports[0].x, 1.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].y, 3.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].minDepth, 5.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].width, 6.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].height, 8.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].maxDepth, 14.0f); + CORRADE_COMPARE(info->pViewportState->scissorCount, 1); + CORRADE_VERIFY(info->pViewportState->pScissors); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.x, 15); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.y, 34); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.width, 69); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.height, 38); +} + +void PipelineTest::rasterizationCreateInfoViewport2DScissor() { + ShaderSet shaderSet; + MeshLayout meshLayout{MeshPrimitive::Triangles}; + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, {}, {}, 0, 1}; + info.setViewport({{1.5f, 3.5f}, {8, 12}}, {{15, 34}, {84, 72}}); + + CORRADE_VERIFY(info->pViewportState); + CORRADE_COMPARE(info->pViewportState->viewportCount, 1); + CORRADE_VERIFY(info->pViewportState->pViewports); + CORRADE_COMPARE(info->pViewportState->pViewports[0].x, 1.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].y, 3.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].minDepth, 0.0f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].width, 6.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].height, 8.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].maxDepth, 1.0f); + CORRADE_COMPARE(info->pViewportState->scissorCount, 1); + CORRADE_VERIFY(info->pViewportState->pScissors); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.x, 15); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.y, 34); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.width, 69); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.height, 38); +} + +void PipelineTest::rasterizationCreateInfoViewportImplicitScissor() { + ShaderSet shaderSet; + MeshLayout meshLayout{MeshPrimitive::Triangles}; + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, {}, {}, 0, 1}; + info.setViewport({{1.5f, 3.5f, 5.5f}, {8, 12, 14}}); + + CORRADE_VERIFY(info->pViewportState); + CORRADE_COMPARE(info->pViewportState->viewportCount, 1); + CORRADE_VERIFY(info->pViewportState->pViewports); + CORRADE_COMPARE(info->pViewportState->pViewports[0].x, 1.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].y, 3.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].minDepth, 5.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].width, 6.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].height, 8.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].maxDepth, 14.0f); + CORRADE_COMPARE(info->pViewportState->scissorCount, 1); + CORRADE_VERIFY(info->pViewportState->pScissors); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.x, 1); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.y, 3); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.width, 7); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.height, 9); +} + +void PipelineTest::rasterizationCreateInfoViewport2DImplicitScissor() { + ShaderSet shaderSet; + MeshLayout meshLayout{MeshPrimitive::Triangles}; + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, {}, {}, 0, 1}; + info.setViewport({{1.5f, 3.5f}, {8, 12}}); + + CORRADE_VERIFY(info->pViewportState); + CORRADE_COMPARE(info->pViewportState->viewportCount, 1); + CORRADE_VERIFY(info->pViewportState->pViewports); + CORRADE_COMPARE(info->pViewportState->pViewports[0].x, 1.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].y, 3.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].minDepth, 0.0f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].width, 6.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].height, 8.5f); + CORRADE_COMPARE(info->pViewportState->pViewports[0].maxDepth, 1.0f); + CORRADE_COMPARE(info->pViewportState->scissorCount, 1); + CORRADE_VERIFY(info->pViewportState->pScissors); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.x, 1); + CORRADE_COMPARE(info->pViewportState->pScissors[0].offset.y, 3); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.width, 7); + CORRADE_COMPARE(info->pViewportState->pScissors[0].extent.height, 9); +} + +void PipelineTest::rasterizationCreateInfoDynamicState() { + MeshLayout meshLayout{MeshPrimitive::Triangles}; + RasterizationPipelineCreateInfo info{ShaderSet{}, meshLayout, VkPipelineLayout{}, VkRenderPass{}, 0, 0}; + CORRADE_VERIFY(!info->pDynamicState); + + info.setDynamicStates( + DynamicRasterizationState::CullMode| + DynamicRasterizationState::DepthBoundsTestEnable| + DynamicRasterizationState::ScissorWithCount); + + CORRADE_VERIFY(info->pDynamicState); + CORRADE_COMPARE(info->pDynamicState->dynamicStateCount, 3); + CORRADE_VERIFY(info->pDynamicState->pDynamicStates); + CORRADE_COMPARE(info->pDynamicState->pDynamicStates[0], VK_DYNAMIC_STATE_CULL_MODE_EXT); + CORRADE_COMPARE(info->pDynamicState->pDynamicStates[1], VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT); + CORRADE_COMPARE(info->pDynamicState->pDynamicStates[2], VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT); +} + +void PipelineTest::constructNoCreate() { + { + Pipeline pipeline{NoCreate}; + CORRADE_VERIFY(!pipeline.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void PipelineTest::constructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + void PipelineTest::memoryBarrierConstruct() { MemoryBarrier barrier{Access::ColorAttachmentWrite|Access::DepthStencilAttachmentWrite, Access::TransferRead}; CORRADE_COMPARE(barrier->srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 32a73c9ba..798634c67 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -23,20 +23,45 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include +#include + +#include "Magnum/Math/Range.h" #include "Magnum/Vk/BufferCreateInfo.h" #include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPoolCreateInfo.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/ImageCreateInfo.h" +#include "Magnum/Vk/MeshLayout.h" #include "Magnum/Vk/Pipeline.h" +#include "Magnum/Vk/PipelineLayoutCreateInfo.h" #include "Magnum/Vk/PixelFormat.h" +#include "Magnum/Vk/RasterizationPipelineCreateInfo.h" +#include "Magnum/Vk/RenderPassCreateInfo.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/ShaderCreateInfo.h" +#include "Magnum/Vk/ShaderSet.h" +#include "Magnum/Vk/VertexFormat.h" #include "Magnum/Vk/VulkanTester.h" +#include "configure.h" + namespace Magnum { namespace Vk { namespace Test { namespace { struct PipelineVkTest: VulkanTester { explicit PipelineVkTest(); + void constructRasterization(); + void constructRasterizationViewportNotSet(); + void constructRasterizationViewportNotSetDiscardEnabled(); + void constructRasterizationViewportNotSetDynamic(); + void constructMove(); + + void wrap(); + void pipelineBarrier(); void pipelineBarrierExecutionOnly(); void pipelineBarrierGlobalMemory(); @@ -45,13 +70,284 @@ struct PipelineVkTest: VulkanTester { }; PipelineVkTest::PipelineVkTest() { - addTests({&PipelineVkTest::pipelineBarrier, + addTests({&PipelineVkTest::constructRasterization, + &PipelineVkTest::constructRasterizationViewportNotSet, + &PipelineVkTest::constructRasterizationViewportNotSetDiscardEnabled, + &PipelineVkTest::constructRasterizationViewportNotSetDynamic, + &PipelineVkTest::constructMove, + + &PipelineVkTest::wrap, + + &PipelineVkTest::pipelineBarrier, &PipelineVkTest::pipelineBarrierExecutionOnly, &PipelineVkTest::pipelineBarrierGlobalMemory, &PipelineVkTest::pipelineBarrierBufferMemory, &PipelineVkTest::pipelineBarrierImageMemory}); } +using namespace Containers::Literals; + +void PipelineVkTest::constructRasterization() { + /* Wonderful, this contains basically EVERYTHING ELSE that got implemented + until now. */ + + { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs + so playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}) + }; + CORRADE_VERIFY(pipeline.handle()); + CORRADE_COMPARE(pipeline.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void PipelineVkTest::constructRasterizationViewportNotSet() { + MeshLayout meshLayout{MeshPrimitive::Triangles}; + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + ShaderSet shaderSet; + + RasterizationPipelineCreateInfo info{ + shaderSet, meshLayout, pipelineLayout, {}, 0, 1 + }; + CORRADE_VERIFY(!info->pViewportState); + + std::ostringstream out; + Error redirectError{&out}; + Pipeline pipeline{device(), info}; + CORRADE_COMPARE(out.str(), "Vk::Pipeline: if rasterization discard is not enabled, the viewport has to be either dynamic or set via setViewport()\n"); +} + +void PipelineVkTest::constructRasterizationViewportNotSetDiscardEnabled() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + RasterizationPipelineCreateInfo info{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1}; + CORRADE_VERIFY(!info->pViewportState); + /** @todo switch to Magnum API once exposed */ + const_cast(info->pRasterizationState)->rasterizerDiscardEnable = true; + Pipeline pipeline{device(), info}; + + /* The only thing I want to verify is that this doesn't crash or assert */ + CORRADE_VERIFY(pipeline.handle()); +} + +void PipelineVkTest::constructRasterizationViewportNotSetDynamic() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs + so playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + RasterizationPipelineCreateInfo info{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1}; + info.setViewport(Range3D{}) /* Has to be set because the count is used */ + .setDynamicStates(DynamicRasterizationState::Viewport| + DynamicRasterizationState::Scissor); + /* But the data don't have to be there */ + const_cast(info->pViewportState)->pViewports = nullptr; + const_cast(info->pViewportState)->pScissors = nullptr; + + Pipeline pipeline{device(), info}; + + /* The only thing I want to verify is that this doesn't crash or assert */ + CORRADE_VERIFY(pipeline.handle()); +} + +void PipelineVkTest::constructMove() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + Pipeline a{device(), RasterizationPipelineCreateInfo{ + shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}) + }; + VkPipeline handle = a.handle(); + + Pipeline b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Pipeline c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void PipelineVkTest::wrap() { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + AttachmentDescription{PixelFormat::RGBA8Unorm, + AttachmentLoadOperation::Clear, + AttachmentStoreOperation::Store, + ImageLayout::Undefined, + ImageLayout::ColorAttachment} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({ + AttachmentReference{0, ImageLayout::ColorAttachment} + })) + }; + + /* Not sure if this is really needed, but the shader needs those inputs so + playing it safe */ + MeshLayout meshLayout{MeshPrimitive::Triangles}; + meshLayout + .addBinding(0, 2*4*4) + .addAttribute(0, 0, Vk::VertexFormat::Vector4, 0) + .addAttribute(1, 0, Vk::VertexFormat::Vector4, 4*4); + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")) + }}; + + ShaderSet shaderSet; + shaderSet + .addShader(ShaderStage::Vertex, shader, "ver"_s) + .addShader(ShaderStage::Fragment, shader, "fra"_s); + + VkPipeline pipeline{}; + CORRADE_COMPARE(Result(device()->CreateGraphicsPipelines(device(), {}, 1, + RasterizationPipelineCreateInfo{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} + .setViewport({{}, {200, 200}}), + nullptr, &pipeline)), Result::Success); + + auto wrapped = Pipeline::wrap(device(), pipeline, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), pipeline); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), pipeline); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyPipeline(device(), pipeline, nullptr); +} + void PipelineVkTest::pipelineBarrier() { CommandPool pool{device(), CommandPoolCreateInfo{ device().properties().pickQueueFamily(QueueFlag::Graphics)}}; diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 7f93d1b61..652049c49 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -62,6 +62,8 @@ enum class DeviceFeature: UnsignedShort; typedef Containers::BigEnumSet DeviceFeatures; class DeviceProperties; enum class DeviceType: Int; +enum class DynamicRasterizationState: UnsignedByte; +typedef Containers::BigEnumSet DynamicRasterizationStates; class Extension; class ExtensionProperties; class Fence; @@ -96,6 +98,7 @@ enum class MemoryHeapFlag: UnsignedInt; typedef Containers::EnumSet MemoryHeapFlags; class MeshLayout; enum class MeshPrimitive: Int; +class Pipeline; class PipelineLayout; class PipelineLayoutCreateInfo; enum class PipelineStage: UnsignedInt; @@ -104,6 +107,7 @@ enum class PixelFormat: Int; class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; +class RasterizationPipelineCreateInfo; class RenderPass; class RenderPassBeginInfo; class RenderPassCreateInfo;