diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp index ceef6f531..e4e926ee7 100644 --- a/src/Magnum/Vk/Pipeline.cpp +++ b/src/Magnum/Vk/Pipeline.cpp @@ -43,7 +43,12 @@ namespace Magnum { namespace Vk { struct RasterizationPipelineCreateInfo::State { Containers::Array colorBlendAttachments; - Containers::Array dynamicStates; + + /* The enum is saved as well to be subsequently available through + Pipeline::dynamicRasterizationStates() */ + DynamicRasterizationStates dynamicStates; + Containers::Array dynamicStateList; + /** @todo make an array once we support multiview */ VkViewport viewport; VkRect2D scissor; @@ -242,21 +247,24 @@ constexpr VkDynamicState DynamicRasterizationStateMapping[]{ } RasterizationPipelineCreateInfo& RasterizationPipelineCreateInfo::setDynamicStates(const DynamicRasterizationStates& states) { + /* Save the enum so we can store it in the created Pipeline object later */ + _state->dynamicStates = 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}; + _state->dynamicStateList = 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]; + _state->dynamicStateList[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; + _dynamicInfo.pDynamicStates = _state->dynamicStateList; _info.pDynamicState = &_dynamicInfo; return *this; } @@ -278,15 +286,20 @@ ComputePipelineCreateInfo::ComputePipelineCreateInfo(const VkComputePipelineCrea member instead of doing a copy */ _info(info) {} -Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const HandleFlags flags) { +Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const DynamicRasterizationStates& dynamicStates, const HandleFlags flags) { Pipeline out{NoCreate}; out._device = &device; out._handle = handle; out._bindPoint = bindPoint; out._flags = flags; + out._dynamicStates.rasterization = dynamicStates; return out; } +Pipeline Pipeline::wrap(Device& device, const PipelineBindPoint bindPoint, const VkPipeline handle, const HandleFlags flags) { + return wrap(device, bindPoint, handle, DynamicRasterizationStates{}, flags); +} + Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): _device{&device}, #ifdef CORRADE_GRACEFUL_ASSERT @@ -294,7 +307,8 @@ Pipeline::Pipeline(Device& device, const RasterizationPipelineCreateInfo& info): _handle{}, #endif _bindPoint{PipelineBindPoint::Rasterization}, - _flags{HandleFlag::DestroyOnDestruction} + _flags{HandleFlag::DestroyOnDestruction}, + _dynamicStates{info._state->dynamicStates} { /* Doesn't check that the viewport is really a dynamic state, but should catch most cases without false positives */ @@ -304,13 +318,17 @@ 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}, _bindPoint{PipelineBindPoint::Compute}, _flags{HandleFlag::DestroyOnDestruction} { +Pipeline::Pipeline(Device& device, const ComputePipelineCreateInfo& info): _device{&device}, _bindPoint{PipelineBindPoint::Compute}, _flags{HandleFlag::DestroyOnDestruction}, _dynamicStates{} { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateComputePipelines(device, {}, 1, info, nullptr, &_handle)); } -Pipeline::Pipeline(NoCreateT): _device{}, _handle{}, _bindPoint{} {} +Pipeline::Pipeline(NoCreateT): _device{}, _handle{}, _bindPoint{}, _dynamicStates{} {} -Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _bindPoint{other._bindPoint}, _flags{other._flags} { +Pipeline::Pipeline(Pipeline&& other) noexcept: _device{other._device}, _handle{other._handle}, _bindPoint{other._bindPoint}, _flags{other._flags}, + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _dynamicStates(other._dynamicStates) +{ other._handle = {}; } @@ -325,9 +343,16 @@ Pipeline& Pipeline::operator=(Pipeline&& other) noexcept { swap(other._handle, _handle); swap(other._bindPoint, _bindPoint); swap(other._flags, _flags); + swap(other._dynamicStates, _dynamicStates); return *this; } +DynamicRasterizationStates Pipeline::dynamicRasterizationStates() const { + CORRADE_ASSERT(_bindPoint == PipelineBindPoint::Rasterization, + "Vk::Pipeline::dynamicRasterizationStates(): not a rasterization pipeline", {}); + return _dynamicStates.rasterization; +} + VkPipeline Pipeline::release() { const VkPipeline handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 554968675..1026adb2f 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -30,6 +30,7 @@ * @m_since_latest */ +#include #include #include "Magnum/Magnum.h" @@ -116,6 +117,9 @@ class MAGNUM_VK_EXPORT Pipeline { * @param bindPoint Pipeline bind point. Available through * @ref bindPoint() afterwards. * @param handle The @type_vk{Pipeline} handle + * @param dynamicStates Dynamic states enabled on the rasterization + * pipeline. Available through @ref dynamicRasterizationStates() + * afterwards. * @param flags Handle flags * * The @p handle is expected to be originating from @p device. Unlike @@ -124,6 +128,8 @@ class MAGNUM_VK_EXPORT Pipeline { * different behavior. * @see @ref release() */ + static Pipeline wrap(Device& device, PipelineBindPoint bindPoint, VkPipeline handle, const DynamicRasterizationStates& dynamicStates, HandleFlags flags = {}); + /** @overload */ static Pipeline wrap(Device& device, PipelineBindPoint bindPoint, VkPipeline handle, HandleFlags flags = {}); /** @@ -193,6 +199,15 @@ class MAGNUM_VK_EXPORT Pipeline { */ PipelineBindPoint bindPoint() const { return _bindPoint; } + /** + * @brief Dynamic rasterization states enabled in this pipeline + * + * Contains the states passed to @ref RasterizationPipelineCreateInfo::setDynamicStates() + * or to the @ref wrap() call. Expects that @ref bindPoint() is + * @ref PipelineBindPoint::Rasterization. + */ + DynamicRasterizationStates dynamicRasterizationStates() const; + /** * @brief Release the underlying Vulkan pipeline * @@ -210,6 +225,9 @@ class MAGNUM_VK_EXPORT Pipeline { VkPipeline _handle; PipelineBindPoint _bindPoint; HandleFlags _flags; + union { + DynamicRasterizationStates rasterization; + } _dynamicStates; }; /** diff --git a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h index e579212bc..a5ce38bdf 100644 --- a/src/Magnum/Vk/RasterizationPipelineCreateInfo.h +++ b/src/Magnum/Vk/RasterizationPipelineCreateInfo.h @@ -524,6 +524,8 @@ class MAGNUM_VK_EXPORT RasterizationPipelineCreateInfo { operator const VkGraphicsPipelineCreateInfo*() const { return &_info; } private: + friend Pipeline; + VkGraphicsPipelineCreateInfo _info; VkPipelineViewportStateCreateInfo _viewportInfo; VkPipelineRasterizationStateCreateInfo _rasterizationInfo; diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp index 98c3cf403..6fd80df60 100644 --- a/src/Magnum/Vk/Test/PipelineVkTest.cpp +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -62,7 +62,10 @@ struct PipelineVkTest: VulkanTester { void constructCompute(); void constructMove(); - void wrap(); + void wrapRasterization(); + void wrapCompute(); + + void dynamicRasterizationStatesNotRasterization(); void cmdBind(); @@ -81,7 +84,10 @@ PipelineVkTest::PipelineVkTest() { &PipelineVkTest::constructCompute, &PipelineVkTest::constructMove, - &PipelineVkTest::wrap, + &PipelineVkTest::wrapRasterization, + &PipelineVkTest::wrapCompute, + + &PipelineVkTest::dynamicRasterizationStatesNotRasterization, &PipelineVkTest::cmdBind, @@ -134,10 +140,12 @@ void PipelineVkTest::constructRasterization() { Pipeline pipeline{device(), RasterizationPipelineCreateInfo{ shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) }; CORRADE_VERIFY(pipeline.handle()); CORRADE_COMPARE(pipeline.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(pipeline.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(pipeline.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); } /* Shouldn't crash or anything */ @@ -241,16 +249,16 @@ void PipelineVkTest::constructRasterizationViewportNotSetDynamic() { 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); + .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 */ + /* The main thing I want to verify is that this doesn't crash or assert */ CORRADE_VERIFY(pipeline.handle()); + CORRADE_COMPARE(pipeline.dynamicRasterizationStates(), DynamicRasterizationState::Viewport|DynamicRasterizationState::Scissor); } void PipelineVkTest::constructCompute() { @@ -311,6 +319,7 @@ void PipelineVkTest::constructMove() { Pipeline a{device(), RasterizationPipelineCreateInfo{ shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias) }; VkPipeline handle = a.handle(); @@ -319,6 +328,7 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(b.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(b.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); Pipeline c{NoCreate}; c = std::move(b); @@ -327,12 +337,13 @@ void PipelineVkTest::constructMove() { CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(c.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(c.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } -void PipelineVkTest::wrap() { +void PipelineVkTest::wrapRasterization() { RenderPass renderPass{device(), RenderPassCreateInfo{} .setAttachments({ AttachmentDescription{PixelFormat::RGBA8Unorm, @@ -368,12 +379,39 @@ void PipelineVkTest::wrap() { VkPipeline pipeline{}; CORRADE_COMPARE(Result(device()->CreateGraphicsPipelines(device(), {}, 1, RasterizationPipelineCreateInfo{shaderSet, meshLayout, pipelineLayout, renderPass, 0, 1} - .setViewport({{}, {200, 200}}), + .setViewport({{}, {200, 200}}) + .setDynamicStates(DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias), nullptr, &pipeline)), Result::Success); - auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Rasterization, pipeline, HandleFlag::DestroyOnDestruction); + auto wrapped = Pipeline::wrap(device(), PipelineBindPoint::Rasterization, pipeline, DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias, HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(wrapped.handle(), pipeline); CORRADE_COMPARE(wrapped.bindPoint(), PipelineBindPoint::Rasterization); + CORRADE_COMPARE(wrapped.dynamicRasterizationStates(), DynamicRasterizationState::LineWidth|DynamicRasterizationState::DepthBias); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), pipeline); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyPipeline(device(), pipeline, nullptr); +} + +void PipelineVkTest::wrapCompute() { + 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); + + VkPipeline pipeline{}; + CORRADE_COMPARE(Result(device()->CreateComputePipelines(device(), {}, 1, + ComputePipelineCreateInfo{shaderSet, pipelineLayout}, + nullptr, &pipeline)), Result::Success); + + 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); @@ -381,6 +419,30 @@ void PipelineVkTest::wrap() { device()->DestroyPipeline(device(), pipeline, nullptr); } +void PipelineVkTest::dynamicRasterizationStatesNotRasterization() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + 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 + }}; + + std::ostringstream out; + Error redirectError{&out}; + pipeline.dynamicRasterizationStates(); + CORRADE_COMPARE(out.str(), "Vk::Pipeline::dynamicRasterizationStates(): not a rasterization pipeline\n"); +} + void PipelineVkTest::cmdBind() { CommandPool pool{device(), CommandPoolCreateInfo{ /* This might blow up if queue() isn't the one matching this family */