diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index eed64d40b..6cf1ecffc 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -830,6 +830,17 @@ Vk::Pipeline pipeline{device, Vk::ComputePipelineCreateInfo{ /* [Pipeline-creation-compute] */ } +{ +Vk::CommandBuffer cmd{NoCreate}; +/* [Pipeline-usage] */ +Vk::Pipeline pipeline{DOXYGEN_IGNORE(NoCreate)}; + +DOXYGEN_IGNORE() + +cmd.bindPipeline(pipeline); +/* [Pipeline-usage] */ +} + { 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 eff43902e..29b563d7b 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -114,7 +114,7 @@ Vulkan function | Matching API @fn_vk{CmdBeginRenderPass}, \n @fn_vk{CmdBeginRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{CmdNextSubpass}, \n @fn_vk{CmdNextSubpass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{CmdEndRenderpass}, \n @fn_vk{CmdEndRenderpass2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref CommandBuffer::beginRenderPass(), \n @ref CommandBuffer::nextSubpass(), \n @ref CommandBuffer::endRenderPass() @fn_vk{CmdBindDescriptorSets} | | @fn_vk{CmdBindIndexBuffer} | | -@fn_vk{CmdBindPipeline} | | +@fn_vk{CmdBindPipeline} | @ref CommandBuffer::bindPipeline() @fn_vk{CmdBindVertexBuffers}, \n @fn_vk{CmdBindVertexBuffers2EXT} @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CmdBlitImage}, \n @fn_vk{CmdBlitImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdBuildAccelerationStructuresIndirectKHR} @m_class{m-label m-flat m-warning} **KHR** | | @@ -915,7 +915,7 @@ Vulkan enum | Matching API --------------------------------------- | ------------ @type_vk{PeerMemoryFeatureFlagBits} @m_class{m-label m-flat m-success} **KHR, 1.1**, \n @type_vk{PeerMemoryFeatureFlags} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{PhysicalDeviceType} | @ref DeviceType -@type_vk{PipelineBindPoint} | | +@type_vk{PipelineBindPoint} | @ref PipelineBindPoint @type_vk{PipelineCacheCreateFlagBits}, \n @type_vk{PipelineCacheCreateFlags} | | @type_vk{PipelineCacheHeaderVersion} | | @type_vk{PipelineCacheCreateFlagBits}, \n @type_vk{PipelineCacheCreateFlags} | | diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index 328bebacd..cd8440668 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -357,6 +357,16 @@ class MAGNUM_VK_EXPORT CommandBuffer { CommandBuffer& endRenderPass(); #endif + /** + * @brief Bind a pipeline + * @return Reference to self (for method chaining) + * + * Can be called both inside and outside a render pass. See + * @ref Vk-Pipeline-usage for a usage example. + * @see @fn_vk_keyword{CmdBindPipeline} + */ + CommandBuffer& bindPipeline(Pipeline& pipeline); + /** * @brief Insert an execution barrier with optional memory dependencies * @param sourceStages Source stages. Has to contain at least diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index 4183ab314..05f2d80ba 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -277,10 +277,11 @@ ComputePipelineCreateInfo::ComputePipelineCreateInfo(const VkComputePipelineCrea member instead of doing a copy */ _info(info) {} -Pipeline Pipeline::wrap(Device& device, const VkPipeline handle, const HandleFlags flags) { +Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const HandleFlags flags) { Pipeline out{NoCreate}; out._device = &device; out._handle = handle; + out._bindPoint = bindPoint; out._flags = flags; return out; } @@ -291,6 +292,7 @@ Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): /* Otherwise vkDestroyPipeline() crashes when we hit the assert */ _handle{}, #endif + _bindPoint{PipelineBindPoint::Rasterization}, _flags{HandleFlag::DestroyOnDestruction} { /* Doesn't check that the viewport is really a dynamic state, but should @@ -301,13 +303,13 @@ Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateGraphicsPipelines(device, {}, 1, info, nullptr, &_handle)); } -Pipeline::Pipeline(Device& device, const ComputePipelineCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { +Pipeline::Pipeline(Device& device, const ComputePipelineCreateInfo& info): _device{&device}, _bindPoint{PipelineBindPoint::Compute}, _flags{HandleFlag::DestroyOnDestruction} { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateComputePipelines(device, {}, 1, info, nullptr, &_handle)); } -Pipeline::Pipeline(NoCreateT): _device{}, _handle{} {} +Pipeline::Pipeline(NoCreateT): _device{}, _handle{}, _bindPoint{} {} -Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { +Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _bindPoint{other._bindPoint}, _flags{other._flags} { other._handle = {}; } @@ -320,6 +322,7 @@ Pipeline& Pipeline::operator=(Pipeline&& other) noexcept { using std::swap; swap(other._device, _device); swap(other._handle, _handle); + swap(other._bindPoint, _bindPoint); swap(other._flags, _flags); return *this; } @@ -382,6 +385,11 @@ ImageMemoryBarrier::ImageMemoryBarrier(const VkImageMemoryBarrier& barrier): member instead of doing a copy */ _barrier(barrier) {} +CommandBuffer& CommandBuffer::bindPipeline(Pipeline& pipeline) { + (**_device).CmdBindPipeline(_handle, VkPipelineBindPoint(pipeline.bindPoint()), pipeline); + return *this; +} + CommandBuffer& CommandBuffer::pipelineBarrier(const PipelineStages sourceStages, const PipelineStages destinationStages, const Containers::ArrayView memoryBarriers, const Containers::ArrayView bufferMemoryBarriers, const Containers::ArrayView imageMemoryBarriers, const DependencyFlags dependencyFlags) { /* Once these grow (VkSampleLocationsInfoEXT?), they will need to be linearized into a separate array first */ @@ -426,4 +434,21 @@ CommandBuffer& CommandBuffer::pipelineBarrier(const PipelineStages sourceStages, return pipelineBarrier(sourceStages, destinationStages, Containers::arrayView(imageMemoryBarriers), dependencyFlags); } +Debug& operator<<(Debug& debug, const PipelineBindPoint value) { + debug << "Vk::PipelineBindPoint" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::PipelineBindPoint::value: return debug << "::" << Debug::nospace << #value; + _c(Rasterization) + _c(RayTracing) + _c(Compute) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Vulkan docs have the values in decimal, so not converting to hex */ + return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; +} + }} diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 5f3e9911a..554968675 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -26,7 +26,7 @@ */ /** @file - * @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 + * @brief Class @ref Magnum::Vk::Pipeline, @ref Magnum::Vk::MemoryBarrier, @ref Magnum::Vk::BufferMemoryBarrier, @ref Magnum::Vk::ImageMemoryBarrier, enum @ref Magnum::Vk::PipelineBindPoint, @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,37 @@ namespace Magnum { namespace Vk { +/** +@brief Pipeline bind point +@m_since_latest + +Wraps a @type_vk_keyword{PipelineBindPoint}. +@see @ref Pipeline::bindPoint() +@m_enum_values_as_keywords +*/ +enum class PipelineBindPoint: Int { + /** + * Rasterization pipeline + * @see @ref RasterizationPipelineCreateInfo + */ + Rasterization = VK_PIPELINE_BIND_POINT_GRAPHICS, + + /** Ray tracing pipeline */ + RayTracing = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, + + /** + * Compute pipeline + * @see @ref ComputePipelineCreateInfo + */ + Compute = VK_PIPELINE_BIND_POINT_COMPUTE +}; + +/** +@debugoperatorenum{PipelineBindPoint} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, PipelineBindPoint value); + /** @brief Pipeline @m_since_latest @@ -68,12 +99,22 @@ a @ref ShaderSet containing a single @ref ShaderStage::Compute shader and a @link PipelineLayout @endlink: @snippet MagnumVk.cpp Pipeline-creation-compute + +@section Vk-Pipeline-usage Pipeline usage + +A pipeline is bound to a compatible command buffer using +@ref CommandBuffer::bindPipeline(), which replaces any pipeline bound +previously to the same @ref bindPoint(): + +@snippet MagnumVk.cpp Pipeline-usage */ class MAGNUM_VK_EXPORT Pipeline { public: /** * @brief Wrap existing Vulkan handle * @param device Vulkan device the pipeline is created on + * @param bindPoint Pipeline bind point. Available through + * @ref bindPoint() afterwards. * @param handle The @type_vk{Pipeline} handle * @param flags Handle flags * @@ -83,13 +124,14 @@ class MAGNUM_VK_EXPORT Pipeline { * different behavior. * @see @ref release() */ - static Pipeline wrap(Device& device, VkPipeline handle, HandleFlags flags = {}); + static Pipeline wrap(Device& device, PipelineBindPoint bindPoint, VkPipeline handle, HandleFlags flags = {}); /** * @brief Construct a rasterization pipeline * @param device Vulkan device to create the pipeline on * @param info Rasterization pipeline creation info * + * THe @ref bindPoint() is set to @ref PipelineBindPoint::Rasterization. * @see @fn_vk_keyword{CreateGraphicsPipelines} */ explicit Pipeline(Device& device, const RasterizationPipelineCreateInfo& info); @@ -99,6 +141,7 @@ class MAGNUM_VK_EXPORT Pipeline { * @param device Vulkan device to create the pipeline on * @param info Compite pipeline creation info * + * THe @ref bindPoint() is set to @ref PipelineBindPoint::Compute. * @see @fn_vk_keyword{CreateComputePipelines} */ explicit Pipeline(Device& device, const ComputePipelineCreateInfo& info); @@ -142,6 +185,14 @@ class MAGNUM_VK_EXPORT Pipeline { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Pipeline bind point + * + * Either set implicitly based on what constructor was used, or coming + * from the @ref wrap() call. + */ + PipelineBindPoint bindPoint() const { return _bindPoint; } + /** * @brief Release the underlying Vulkan pipeline * @@ -157,6 +208,7 @@ class MAGNUM_VK_EXPORT Pipeline { Device* _device; VkPipeline _handle; + PipelineBindPoint _bindPoint; HandleFlags _flags; }; diff --git a/src/Magnum/Vk/RenderPass.cpp b/src/Magnum/Vk/RenderPass.cpp index a849d4639..bbe038415 100644 --- a/src/Magnum/Vk/RenderPass.cpp +++ b/src/Magnum/Vk/RenderPass.cpp @@ -38,6 +38,7 @@ #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Image.h" #include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/Pipeline.h" #include "Magnum/Vk/PixelFormat.h" #include "Magnum/Vk/Implementation/DeviceState.h" @@ -175,7 +176,7 @@ struct SubpassDescription::State { SubpassDescription::SubpassDescription(const Flags flags): _description{} { _description.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2; _description.flags = VkSubpassDescriptionFlags(flags); - _description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + _description.pipelineBindPoint = VkPipelineBindPoint(PipelineBindPoint::Rasterization); } SubpassDescription::SubpassDescription(NoInitT) noexcept {} diff --git a/src/Magnum/Vk/RenderPassCreateInfo.h b/src/Magnum/Vk/RenderPassCreateInfo.h index 27332593c..1d26a764e 100644 --- a/src/Magnum/Vk/RenderPassCreateInfo.h +++ b/src/Magnum/Vk/RenderPassCreateInfo.h @@ -506,7 +506,7 @@ class MAGNUM_VK_EXPORT SubpassDescription { * in addition to `sType`, everything else is zero-filled: * * - `flags` - * - `pipelineBindPoint` to @val_vk{PIPELINE_BIND_POINT_GRAPHICS,PipelineBindPoint} + * - `pipelineBindPoint` to @ref PipelineBindPoint::Rasterization * * Use @ref setInputAttachments(), @ref setColorAttachments(), * @ref setDepthStencilAttachment() and @ref setPreserveAttachments() diff --git a/src/Magnum/Vk/Test/PipelineTest.cpp b/src/Magnum/Vk/Test/PipelineTest.cpp index 95ca5eda1..a5df9d156 100644 --- a/src/Magnum/Vk/Test/PipelineTest.cpp +++ b/src/Magnum/Vk/Test/PipelineTest.cpp @@ -82,6 +82,8 @@ struct PipelineTest: TestSuite::Tester { void imageMemoryBarrierConstructImplicitAspect(); void imageMemoryBarrierConstructNoInit(); void imageMemoryBarrierConstructFromVk(); + + void debugBindPoint(); }; PipelineTest::PipelineTest() { @@ -120,7 +122,9 @@ PipelineTest::PipelineTest() { &PipelineTest::imageMemoryBarrierConstruct, &PipelineTest::imageMemoryBarrierConstructImplicitAspect, &PipelineTest::imageMemoryBarrierConstructNoInit, - &PipelineTest::imageMemoryBarrierConstructFromVk}); + &PipelineTest::imageMemoryBarrierConstructFromVk, + + &PipelineTest::debugBindPoint}); } using namespace Containers::Literals; @@ -711,6 +715,12 @@ void PipelineTest::imageMemoryBarrierConstructFromVk() { CORRADE_COMPARE(barrier->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); } +void PipelineTest::debugBindPoint() { + std::ostringstream out; + Debug{&out} << PipelineBindPoint::Compute << PipelineBindPoint(-10007655); + CORRADE_COMPARE(out.str(), "Vk::PipelineBindPoint::Compute Vk::PipelineBindPoint(-10007655)\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::PipelineTest) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 2bc5f0534..d365cf630 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -64,6 +64,8 @@ struct PipelineVkTest: VulkanTester { void wrap(); + void cmdBind(); + void cmdBarrier(); void cmdBarrierExecutionOnly(); void cmdBarrierGlobalMemory(); @@ -81,6 +83,8 @@ PipelineVkTest::PipelineVkTest() { &PipelineVkTest::wrap, + &PipelineVkTest::cmdBind, + &PipelineVkTest::cmdBarrier, &PipelineVkTest::cmdBarrierExecutionOnly, &PipelineVkTest::cmdBarrierGlobalMemory, @@ -133,6 +137,7 @@ void PipelineVkTest::constructRasterization() { }; CORRADE_VERIFY(pipeline.handle()); CORRADE_COMPARE(pipeline.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(pipeline.bindPoint(), PipelineBindPoint::Rasterization); } /* Shouldn't crash or anything */ @@ -289,6 +294,7 @@ void PipelineVkTest::constructMove() { CORRADE_VERIFY(!a.handle()); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(b.bindPoint(), PipelineBindPoint::Compute); Pipeline c{NoCreate}; c = std::move(b); @@ -296,6 +302,7 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(c.bindPoint(), PipelineBindPoint::Compute); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); @@ -316,8 +323,9 @@ void PipelineVkTest::wrap() { ComputePipelineCreateInfo{shaderSet, pipelineLayout}, nullptr, &pipeline)), Result::Success); - auto wrapped = Pipeline::wrap(device(), pipeline, HandleFlag::DestroyOnDestruction); + auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Compute, pipeline, HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(wrapped.handle(), pipeline); + CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Compute); /* Release the handle again, destroy by hand */ CORRADE_COMPARE(wrapped.release(), pipeline); @@ -325,6 +333,33 @@ void PipelineVkTest::wrap() { device()->DestroyPipeline(device(), pipeline, nullptr); } +void PipelineVkTest::cmdBind() { + CommandPool pool{device(), CommandPoolCreateInfo{ + /* This might blow up if queue() isn't the one matching this family */ + device().properties().pickQueueFamily(QueueFlag::Graphics|QueueFlag::Compute)}}; + + PipelineLayout pipelineLayout{device(), PipelineLayoutCreateInfo{}}; + + Shader shader{device(), ShaderCreateInfo{ + Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "compute-noop.spv")) + }}; + + ShaderSet shaderSet; + shaderSet.addShader(ShaderStage::Compute, shader, "main"_s); + + Pipeline pipeline{device(), ComputePipelineCreateInfo{ + shaderSet, pipelineLayout + }}; + + pool.allocate() + .begin() + .bindPipeline(pipeline) + .end(); + + /* Does not do anything visible, so just test that it didn't blow up */ + CORRADE_VERIFY(true); +} + void PipelineVkTest::cmdBarrier() { CommandPool pool{device(), CommandPoolCreateInfo{ device().properties().pickQueueFamily(QueueFlag::Graphics)}}; diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 7c2f639f8..50207fcdb 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -100,6 +100,7 @@ typedef Containers::EnumSet MemoryHeapFlags; class MeshLayout; enum class MeshPrimitive: Int; class Pipeline; +enum class PipelineBindPoint: Int; class PipelineLayout; class PipelineLayoutCreateInfo; enum class PipelineStage: UnsignedInt;