From 4a49b3a77001c703b73b5fe7ab1e2966019dee70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 31 Dec 2020 00:37:30 +0100 Subject: [PATCH] Vk: implement render pass begin / end / next commands. --- doc/vulkan-mapping.dox | 9 +- doc/vulkan-support.dox | 2 +- src/Magnum/Vk/CommandBuffer.cpp | 73 ++++++ src/Magnum/Vk/CommandBuffer.h | 74 ++++++ src/Magnum/Vk/Implementation/DeviceState.cpp | 10 + src/Magnum/Vk/Implementation/DeviceState.h | 9 +- src/Magnum/Vk/RenderPass.cpp | 100 ++++++++ src/Magnum/Vk/RenderPass.h | 251 ++++++++++++++++++- src/Magnum/Vk/RenderPassCreateInfo.h | 5 +- src/Magnum/Vk/Test/RenderPassTest.cpp | 162 +++++++++++- src/Magnum/Vk/Test/RenderPassVkTest.cpp | 65 ++++- src/Magnum/Vk/Vk.h | 3 + 12 files changed, 752 insertions(+), 11 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index f0eca200a..ded3fca07 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -75,7 +75,7 @@ Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{CmdBeginQuery}, \n @fn_vk{CmdEndQuery} | | @fn_vk{CmdBeginDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{CmdEndebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | -@fn_vk{CmdBeginRenderPass}, \n @fn_vk{CmdBeginRenderPass2} @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** | | +@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} | | @@ -108,7 +108,6 @@ Vulkan function | Matching API @fn_vk{CmdExecuteCommands} | | @fn_vk{CmdFillBuffer} | | @fn_vk{CmdInsertDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | -@fn_vk{CmdNextSubpass}, \n @fn_vk{CmdNextSubpass2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @fn_vk{CmdPipelineBarrier} | | @fn_vk{CmdPushConstants} | | @fn_vk{CmdResetEvent} | | @@ -635,7 +634,7 @@ Vulkan structure | Matching API @type_vk{RayTracingPipelineInterfaceCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{RayTracingShaderGroupCreateInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{Rect2D} | convertible from/to @ref Range2Di using @ref Magnum/Vk/Integration.h -@type_vk{RenderPassBeginInfo} | | +@type_vk{RenderPassBeginInfo} | @ref RenderPassBeginInfo @type_vk{RenderPassAttachmentBeginInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{RenderPassCreateInfo}, \n @type_vk{RenderPassCreateInfo2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref RenderPassCreateInfo @type_vk{RenderPassMultiviewCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @@ -669,8 +668,8 @@ Vulkan structure | Matching API @type_vk{StencilOpState} | | @type_vk{StridedDeviceAddressRegionKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{SubmitInfo} | | -@type_vk{SubpassBeginInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{SubpassEndInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@type_vk{SubpassBeginInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassBeginInfo +@type_vk{SubpassEndInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassEndInfo @type_vk{SubpassDependency}, \n @type_vk{SubpassDependency2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassDependency @type_vk{SubpassDescription}, \n @type_vk{SubpassDescription2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassDescription @type_vk{SubpassDescriptionDepthStencilResolve} @m_class{m-label m-flat m-success} **KHR, 1.2** | | diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index f0361512a..3f7f5daf9 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -87,7 +87,7 @@ Extension | Status @vk_extension{KHR,sampler_mirror_clamp_to_edge} | @ref Vk::vkSamplerAddressMode() only @vk_extension{KHR,shader_float16_int8} | | @vk_extension{KHR,imageless_framebuffer} | | -@vk_extension{KHR,create_renderpass2} | only render pass creation +@vk_extension{KHR,create_renderpass2} | done @vk_extension{EXT,sampler_filter_minmax} | | @vk_extension{KHR,image_format_list} | | @vk_extension{EXT,descriptor_indexing} | | diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp index e5261e783..6dd723dc2 100644 --- a/src/Magnum/Vk/CommandBuffer.cpp +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -28,6 +28,8 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/RenderPass.h" +#include "Magnum/Vk/Implementation/DeviceState.h" namespace Magnum { namespace Vk { @@ -81,6 +83,77 @@ CommandBuffer& CommandBuffer::begin(const CommandBufferBeginInfo& info) { return *this; } +CommandBuffer& CommandBuffer::beginRenderPass(const RenderPassBeginInfo& info, const SubpassBeginInfo& beginInfo) { + _device->state().cmdBeginRenderPassImplementation(*this, *info, *beginInfo); + return *this; +} + +void CommandBuffer::beginRenderPassImplementationDefault(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdBeginRenderPass(self, &info, beginInfo.contents); +} + +void CommandBuffer::beginRenderPassImplementationKHR(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdBeginRenderPass2KHR(self, &info, &beginInfo); +} + +void CommandBuffer::beginRenderPassImplementation12(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdBeginRenderPass2(self, &info, &beginInfo); +} + +CommandBuffer& CommandBuffer::beginRenderPass(const RenderPassBeginInfo& info) { + return beginRenderPass(info, SubpassBeginInfo{}); +} + +CommandBuffer& CommandBuffer::nextSubpass(const SubpassEndInfo& endInfo, const SubpassBeginInfo& beginInfo) { + _device->state().cmdNextSubpassImplementation(*this, *endInfo, *beginInfo); + return *this; +} + +void CommandBuffer::nextSubpassImplementationDefault(CommandBuffer& self, const VkSubpassEndInfo&, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdNextSubpass(self, beginInfo.contents); +} + +void CommandBuffer::nextSubpassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdNextSubpass2KHR(self, &beginInfo, &endInfo); +} + +void CommandBuffer::nextSubpassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo, const VkSubpassBeginInfo& beginInfo) { + return (**self._device).CmdNextSubpass2(self, &beginInfo, &endInfo); +} + +CommandBuffer& CommandBuffer::nextSubpass(const SubpassEndInfo& endInfo) { + return nextSubpass(endInfo, SubpassBeginInfo{}); +} + +CommandBuffer& CommandBuffer::nextSubpass(const SubpassBeginInfo& beginInfo) { + return nextSubpass(SubpassEndInfo{}, beginInfo); +} + +CommandBuffer& CommandBuffer::nextSubpass() { + return nextSubpass(SubpassBeginInfo{}); +} + +CommandBuffer& CommandBuffer::endRenderPass(const SubpassEndInfo& endInfo) { + _device->state().cmdEndRenderPassImplementation(*this, *endInfo); + return *this; +} + +void CommandBuffer::endRenderPassImplementationDefault(CommandBuffer& self, const VkSubpassEndInfo&) { + return (**self._device).CmdEndRenderPass(self); +} + +void CommandBuffer::endRenderPassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo) { + return (**self._device).CmdEndRenderPass2KHR(self, &endInfo); +} + +void CommandBuffer::endRenderPassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo) { + return (**self._device).CmdEndRenderPass2(self, &endInfo); +} + +CommandBuffer& CommandBuffer::endRenderPass() { + return endRenderPass(SubpassEndInfo{}); +} + void CommandBuffer::end() { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).EndCommandBuffer(_handle)); } diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index 26a178c4a..3d13f7733 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -40,6 +40,8 @@ namespace Magnum { namespace Vk { +namespace Implementation { struct DeviceState; } + /** @brief Command buffer begin info @m_since_latest @@ -267,8 +269,80 @@ class MAGNUM_VK_EXPORT CommandBuffer { */ void end(); + /** + * @brief Begin a new render pass + * @return Reference to self (for method chaining) + * + * If Vulkan 1.2 is not supported and the + * @vk_extension{KHR,create_renderpass2} extension is not enabled on + * the device, only the `contents` field from @p beginInfo is used to + * begin the render pass. + * @see @fn_vk_keyword{CmdBeginRenderPass2}, + * @fn_vk_keyword{CmdBeginRenderPass} + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + CommandBuffer& beginRenderPass(const RenderPassBeginInfo& info, const SubpassBeginInfo& beginInfo = SubpassBeginInfo{}); + #else + /* To avoid dependency on RenderPass.h */ + CommandBuffer& beginRenderPass(const RenderPassBeginInfo& info, const SubpassBeginInfo& beginInfo); + CommandBuffer& beginRenderPass(const RenderPassBeginInfo& info); + #endif + + /** + * @brief Transition to the next subpass of a render pass + * @return Reference to self (for method chaining) + * + * If Vulkan 1.2 is not supported and the + * @vk_extension{KHR,create_renderpass2} extension is not enabled on + * the device, @p endInfo is ignored and only the `contents` field from + * @p beginInfo is used to begin the next subpass + * @see @fn_vk_keyword{CmdNextSubpass2}, + * @fn_vk_keyword{CmdNextSubpass} + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + CommandBuffer& nextSubpass(const SubpassEndInfo& endInfo = SubpassEndInfo{}, const SubpassBeginInfo& beginInfo = SubpassBeginInfo{}); + /** @overload */ + CommandBuffer& nextSubpass(const SubpassBeginInfo& beginInfo); + #else + /* To avoid dependency on RenderPass.h */ + CommandBuffer& nextSubpass(const SubpassEndInfo& endInfo, const SubpassBeginInfo& beginInfo); + CommandBuffer& nextSubpass(const SubpassEndInfo& endInfo); + CommandBuffer& nextSubpass(const SubpassBeginInfo& beginInfo); + CommandBuffer& nextSubpass(); + #endif + + /** + * @brief Transition to the next subpass of a render pass + * @return Reference to self (for method chaining) + * + * If Vulkan 1.2 is not supported and the + * @vk_extension{KHR,create_renderpass2} extension is not enabled on + * the device, @p endInfo is ignored. + * @see @fn_vk_keyword{CmdEndRenderPass2}, + * @fn_vk_keyword{CmdEndRenderPass} + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + CommandBuffer& endRenderPass(const SubpassEndInfo& info = SubpassEndInfo{}); + #else + CommandBuffer& endRenderPass(const SubpassEndInfo& info); + CommandBuffer& endRenderPass(); + #endif + private: friend CommandPool; + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static void beginRenderPassImplementationDefault(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo); + MAGNUM_VK_LOCAL static void beginRenderPassImplementationKHR(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo); + MAGNUM_VK_LOCAL static void beginRenderPassImplementation12(CommandBuffer& self, const VkRenderPassBeginInfo& info, const VkSubpassBeginInfo& beginInfo); + + MAGNUM_VK_LOCAL static void nextSubpassImplementationDefault(CommandBuffer& self, const VkSubpassEndInfo& endInfo, const VkSubpassBeginInfo& beginInfo); + MAGNUM_VK_LOCAL static void nextSubpassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo, const VkSubpassBeginInfo& beginInfo); + MAGNUM_VK_LOCAL static void nextSubpassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo, const VkSubpassBeginInfo& beginInfo); + + MAGNUM_VK_LOCAL static void endRenderPassImplementationDefault(CommandBuffer& self, const VkSubpassEndInfo& endInfo); + MAGNUM_VK_LOCAL static void endRenderPassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo); + MAGNUM_VK_LOCAL static void endRenderPassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo); /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index f6ea5c485..e7e93da1e 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -26,6 +26,7 @@ #include "DeviceState.h" #include "Magnum/Vk/Buffer.h" +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Image.h" @@ -65,10 +66,19 @@ DeviceState::DeviceState(Device& device) { if(device.isVersionSupported(Version::Vk12)) { createRenderPassImplementation = &RenderPass::createImplementation12; + cmdBeginRenderPassImplementation = &CommandBuffer::beginRenderPassImplementation12; + cmdNextSubpassImplementation = &CommandBuffer::nextSubpassImplementation12; + cmdEndRenderPassImplementation = &CommandBuffer::endRenderPassImplementation12; } else if(device.isExtensionEnabled()) { createRenderPassImplementation = &RenderPass::createImplementationKHR; + cmdBeginRenderPassImplementation = &CommandBuffer::beginRenderPassImplementationKHR; + cmdNextSubpassImplementation = &CommandBuffer::nextSubpassImplementationKHR; + cmdEndRenderPassImplementation = &CommandBuffer::endRenderPassImplementationKHR; } else { createRenderPassImplementation = &RenderPass::createImplementationDefault; + cmdBeginRenderPassImplementation = &CommandBuffer::beginRenderPassImplementationDefault; + cmdNextSubpassImplementation = &CommandBuffer::nextSubpassImplementationDefault; + cmdEndRenderPassImplementation = &CommandBuffer::endRenderPassImplementationDefault; } } diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 1b9d6fb43..c35eeb4ad 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -40,12 +40,19 @@ struct DeviceState { explicit DeviceState(Device& instance); void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); - /** @todo put this eventually into a dedicated buffer / image state struct? */ + + /** @todo put the rest eventually into a dedicated buffer / image state / + ... struct? */ + void(*getBufferMemoryRequirementsImplementation)(Device&, const VkBufferMemoryRequirementsInfo2&, VkMemoryRequirements2&); void(*getImageMemoryRequirementsImplementation)(Device&, const VkImageMemoryRequirementsInfo2&, VkMemoryRequirements2&); VkResult(*bindBufferMemoryImplementation)(Device&, UnsignedInt, const VkBindBufferMemoryInfo*); VkResult(*bindImageMemoryImplementation)(Device&, UnsignedInt, const VkBindImageMemoryInfo*); + VkResult(*createRenderPassImplementation)(Device&, const RenderPassCreateInfo&, const VkAllocationCallbacks*, VkRenderPass*); + void(*cmdBeginRenderPassImplementation)(CommandBuffer&, const VkRenderPassBeginInfo&, const VkSubpassBeginInfo&); + void(*cmdNextSubpassImplementation)(CommandBuffer&, const VkSubpassEndInfo&, const VkSubpassBeginInfo&); + void(*cmdEndRenderPassImplementation)(CommandBuffer&, const VkSubpassEndInfo&); }; }}} diff --git a/src/Magnum/Vk/RenderPass.cpp b/src/Magnum/Vk/RenderPass.cpp index d4e14974b..ebd4a368c 100644 --- a/src/Magnum/Vk/RenderPass.cpp +++ b/src/Magnum/Vk/RenderPass.cpp @@ -30,10 +30,12 @@ #include #include +#include "Magnum/Math/Color.h" #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/Implementation/DeviceState.h" namespace Magnum { namespace Vk { @@ -783,4 +785,102 @@ VkResult RenderPass::createImplementation12(Device& device, const RenderPassCrea return device->CreateRenderPass2(device, info, callbacks, handle); } +struct RenderPassBeginInfo::State { + Containers::Array clearValues; +}; + +RenderPassBeginInfo::RenderPassBeginInfo(const VkRenderPass renderPass, const VkFramebuffer framebuffer, const Range2Di& renderArea): _info{} { + _info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + _info.renderPass = renderPass; + _info.framebuffer = framebuffer; + _info.renderArea = VkRect2D(renderArea); +} + +RenderPassBeginInfo::RenderPassBeginInfo(NoInitT) noexcept {} + +RenderPassBeginInfo::RenderPassBeginInfo(const VkRenderPassBeginInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +RenderPassBeginInfo::RenderPassBeginInfo(RenderPassBeginInfo&& 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), + _state{std::move(other._state)} +{ + /* 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.clearValueCount = 0; + other._info.pClearValues = nullptr; +} + +RenderPassBeginInfo::~RenderPassBeginInfo() = default; + +RenderPassBeginInfo& RenderPassBeginInfo::operator=(RenderPassBeginInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._state, _state); + return *this; +} + +RenderPassBeginInfo& RenderPassBeginInfo::clearColor(const UnsignedInt attachment, const Color4& color) { + VkClearValue value; + value.color = VkClearColorValue(color); + return clearInternal(attachment, value); +} + +RenderPassBeginInfo& RenderPassBeginInfo::clearColor(const UnsignedInt attachment, const Vector4i& color) { + VkClearValue value; + value.color = VkClearColorValue(color); + return clearInternal(attachment, value); +} + +RenderPassBeginInfo& RenderPassBeginInfo::clearColor(const UnsignedInt attachment, const Vector4ui& color) { + VkClearValue value; + value.color = VkClearColorValue(color); + return clearInternal(attachment, value); +} + +RenderPassBeginInfo& RenderPassBeginInfo::clearDepthStencil(const UnsignedInt attachment, const Float depth, const UnsignedInt stencil) { + VkClearValue value; + value.depthStencil = {depth, stencil}; + return clearInternal(attachment, value); +} + +RenderPassBeginInfo& RenderPassBeginInfo::clearInternal(const UnsignedInt attachment, const VkClearValue& value) { + if(!_state) _state.emplace(); + if(_state->clearValues.size() <= attachment) + arrayResize(_state->clearValues, Containers::NoInit, attachment + 1); + _state->clearValues[attachment] = value; + _info.clearValueCount = _state->clearValues.size(); + _info.pClearValues = _state->clearValues; + return *this; +} + +SubpassBeginInfo::SubpassBeginInfo(const SubpassContents contents): _info{} { + _info.sType = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO; + _info.contents = VkSubpassContents(contents); +} + +SubpassBeginInfo::SubpassBeginInfo(NoInitT) noexcept {} + +SubpassBeginInfo::SubpassBeginInfo(const VkSubpassBeginInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +SubpassEndInfo::SubpassEndInfo(): _info{} { + _info.sType = VK_STRUCTURE_TYPE_SUBPASS_END_INFO; +} + +SubpassEndInfo::SubpassEndInfo(NoInitT) noexcept {} + +SubpassEndInfo::SubpassEndInfo(const VkSubpassEndInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + }} diff --git a/src/Magnum/Vk/RenderPass.h b/src/Magnum/Vk/RenderPass.h index 1692f3b7a..9115b4ffe 100644 --- a/src/Magnum/Vk/RenderPass.h +++ b/src/Magnum/Vk/RenderPass.h @@ -26,9 +26,11 @@ */ /** @file - * @brief Class @ref Magnum::Vk::RenderPass + * @brief Class @ref Magnum::Vk::RenderPass, @ref Magnum::Vk::RenderPassBeginInfo, @ref Magnum::Vk::SubpassBeginInfo, @ref Magnum::Vk::SubpassEndInfo, enum @ref Magnum::Vk::SubpassContents */ +#include + #include "Magnum/Magnum.h" #include "Magnum/Tags.h" #include "Magnum/Vk/Handle.h" @@ -185,6 +187,253 @@ class MAGNUM_VK_EXPORT RenderPass { HandleFlags _flags; }; +/** +@brief Render pass begin info +@m_since_latest + +Wraps a @type_vk_keyword{RenderPassBeginInfo}. +@see @ref CommandBuffer::beginRenderPass() +*/ +class MAGNUM_VK_EXPORT RenderPassBeginInfo { + public: + /** + * @brief Constructor + * @param renderPass A @ref RenderPass or a raw Vulkan render + * pass handle to begin an instance of + * @param framebuffer A @ref Framebuffer or a raw Vulkan + * framebuffer containing the attachments used with @p renderPass + * @param renderArea Render area affected by the render pass + * instance + * + * The following @type_vk{RenderPassBeginInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `renderPass` + * - `framebuffer` + * - `renderArea` + * + * If there are attachments with @ref AttachmentLoadOperation::Clear + * passed to @ref RenderPassCreateInfo::setAttachments() of + * @p renderPass, you need to call @ref clearColor() / + * @ref clearDepthStencil() with an attachment index corresponding to + * each of them. + */ + explicit RenderPassBeginInfo(VkRenderPass renderPass, VkFramebuffer framebuffer, const Range2Di& renderArea); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit RenderPassBeginInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, 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 RenderPassBeginInfo(const VkRenderPassBeginInfo& info); + + /** @brief Copying is not allowed */ + RenderPassBeginInfo(const RenderPassBeginInfo&) = delete; + + /** @brief Move constructor */ + RenderPassBeginInfo(RenderPassBeginInfo&& other) noexcept; + + ~RenderPassBeginInfo(); + + /** @brief Copying is not allowed */ + RenderPassBeginInfo& operator=(const RenderPassBeginInfo&) = delete; + + /** @brief Move assignment */ + RenderPassBeginInfo& operator=(RenderPassBeginInfo&& other) noexcept; + + /** + * @brief Clear a floating-point or normalized color attachment + * @return Reference to self (for method chaining) + * + * @see @ref AttachmentLoadOperation::Clear + */ + RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Color4& color); + + /** + * @brief Clear a signed integral color attachment + * @return Reference to self (for method chaining) + * + * @see @ref AttachmentLoadOperation::Clear + */ + RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Vector4i& color); + + /** + * @brief Clear an unsigned integral color attachment + * @return Reference to self (for method chaining) + * + * @see @ref AttachmentLoadOperation::Clear + */ + RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Vector4ui& color); + + /** + * @brief Clear a depth/stencil attachment + * @return Reference to self (for method chaining) + * + * If the attachment is not a combined depth/stencil format, the unused + * value is ignored. + * @see @ref AttachmentLoadOperation::Clear + */ + RenderPassBeginInfo& clearDepthStencil(UnsignedInt attachment, Float depth, UnsignedInt stencil); + + /** @brief Underlying @type_vk{RenderPassBeginInfo} structure */ + VkRenderPassBeginInfo& operator*() { return _info; } + /** @overload */ + const VkRenderPassBeginInfo& operator*() const { return _info; } + /** @overload */ + VkRenderPassBeginInfo* operator->() { return &_info; } + /** @overload */ + const VkRenderPassBeginInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkRenderPassBeginInfo*() const { return &_info; } + + private: + RenderPassBeginInfo& clearInternal(UnsignedInt attachment, const VkClearValue& value); + + VkRenderPassBeginInfo _info; + struct State; + Containers::Pointer _state; +}; + +/** +@brief Subpass contents +@m_since_latest + +Wraps @type_vk{SubpassContents}. +@see @ref SubpassBeginInfo::SubpassBeginInfo(), + @ref CommandBuffer::beginRenderPass(), @ref CommandBuffer::nextSubpass() +*/ +enum class SubpassContents: Int { + /** + * Contents of the subpass will be recorded inline in the primary command + * buffer. @ref CommandBufferLevel::Secondary command buffers must not + * be executed within the subpass. This is the default used in the + * @ref SubpassBeginInfo constructor and consequently + * @ref CommandBuffer::beginRenderPass() / @ref CommandBuffer::nextSubpass(). + */ + Inline = VK_SUBPASS_CONTENTS_INLINE, + + /** + * Subpass contents are recorded in @ref CommandBufferLevel::Secondary + * command buffers that will be called from the primary command buffer. + * @todoc reference @fn_vk{CmdExecuteCommands} when exposed and mention + * it's the only allowed command until nextsubpass / renderpass end + */ + SecondaryCommandBuffers = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS +}; + +/** +@brief Subpass begin info +@m_since_latest + +Wraps @type_vk{SubpassBeginInfo}. +@see @ref CommandBuffer::beginRenderPass(), @ref CommandBuffer::nextSubpass() +*/ +class MAGNUM_VK_EXPORT SubpassBeginInfo { + public: + /** + * @brief Constructor + * @param contents How commands in the subpass will be provided + * + * The following @type_vk{SubpassBeginInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `contents` + */ + explicit SubpassBeginInfo(SubpassContents contents = SubpassContents::Inline); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit SubpassBeginInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, 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 SubpassBeginInfo(const VkSubpassBeginInfo& info); + + /** @brief Underlying @type_vk{SubpassBeginInfo} structure */ + VkSubpassBeginInfo& operator*() { return _info; } + /** @overload */ + const VkSubpassBeginInfo& operator*() const { return _info; } + /** @overload */ + VkSubpassBeginInfo* operator->() { return &_info; } + /** @overload */ + const VkSubpassBeginInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkSubpassBeginInfo*() const { return &_info; } + + private: + VkSubpassBeginInfo _info; +}; + +/** +@brief Subpass end info +@m_since_latest + +Wraps @type_vk{SubpassEndInfo}. +@see @ref CommandBuffer::endRenderPass(), @ref CommandBuffer::nextSubpass() +*/ +class MAGNUM_VK_EXPORT SubpassEndInfo { + public: + /** + * @brief Constructor + * + * The following @type_vk{SubpassEndInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - *(none)* + */ + explicit SubpassEndInfo(); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit SubpassEndInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, 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 SubpassEndInfo(const VkSubpassEndInfo& info); + + /** @brief Underlying @type_vk{SubpassEndInfo} structure */ + VkSubpassEndInfo& operator*() { return _info; } + /** @overload */ + const VkSubpassEndInfo& operator*() const { return _info; } + /** @overload */ + VkSubpassEndInfo* operator->() { return &_info; } + /** @overload */ + const VkSubpassEndInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkSubpassEndInfo*() const { return &_info; } + + private: + VkSubpassEndInfo _info; +}; + }} #endif diff --git a/src/Magnum/Vk/RenderPassCreateInfo.h b/src/Magnum/Vk/RenderPassCreateInfo.h index 0da87e081..181f11b17 100644 --- a/src/Magnum/Vk/RenderPassCreateInfo.h +++ b/src/Magnum/Vk/RenderPassCreateInfo.h @@ -64,7 +64,10 @@ enum class AttachmentLoadOperation: Int { /** * Previous contents are cleared to a value specified when a render pass - * instance is begun. + * instance is begun. You're required to provide a clear value for this + * attachment index using @ref RenderPassBeginInfo::clearColor() or + * @ref RenderPassBeginInfo::clearDepthStencil() when you begin render pass + * recording using @ref CommandBuffer::beginRenderPass(). * * @m_class{m-note m-success} * diff --git a/src/Magnum/Vk/Test/RenderPassTest.cpp b/src/Magnum/Vk/Test/RenderPassTest.cpp index a47bcf04f..069898a90 100644 --- a/src/Magnum/Vk/Test/RenderPassTest.cpp +++ b/src/Magnum/Vk/Test/RenderPassTest.cpp @@ -30,7 +30,10 @@ #include #include +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Range.h" #include "Magnum/Vk/Image.h" +#include "Magnum/Vk/Integration.h" #include "Magnum/Vk/RenderPassCreateInfo.h" namespace Magnum { namespace Vk { namespace Test { namespace { @@ -92,6 +95,21 @@ struct RenderPassTest: TestSuite::Tester { void constructNoCreate(); void constructCopy(); + + void beginInfoConstruct(); + void beginInfoConstructNoInit(); + void beginInfoConstructClears(); + void beginInfoConstructFromVk(); + void beginInfoConstructCopy(); + void beginInfoConstructMove(); + + void subpassBeginInfoConstruct(); + void subpassBeginInfoConstructNoInit(); + void subpassBeginInfoConstructFromVk(); + + void subpassEndInfoConstruct(); + void subpassEndInfoConstructNoInit(); + void subpassEndInfoConstructFromVk(); }; RenderPassTest::RenderPassTest() { @@ -164,7 +182,22 @@ RenderPassTest::RenderPassTest() { &RenderPassTest::createInfoConvertToVk, &RenderPassTest::constructNoCreate, - &RenderPassTest::constructCopy}); + &RenderPassTest::constructCopy, + + &RenderPassTest::beginInfoConstruct, + &RenderPassTest::beginInfoConstructNoInit, + &RenderPassTest::beginInfoConstructClears, + &RenderPassTest::beginInfoConstructFromVk, + &RenderPassTest::beginInfoConstructCopy, + &RenderPassTest::beginInfoConstructMove, + + &RenderPassTest::subpassBeginInfoConstruct, + &RenderPassTest::subpassBeginInfoConstructNoInit, + &RenderPassTest::subpassBeginInfoConstructFromVk, + + &RenderPassTest::subpassEndInfoConstruct, + &RenderPassTest::subpassEndInfoConstructNoInit, + &RenderPassTest::subpassEndInfoConstructFromVk}); } template struct Traits; @@ -971,6 +1004,133 @@ void RenderPassTest::constructCopy() { CORRADE_VERIFY(!std::is_copy_assignable{}); } +void RenderPassTest::beginInfoConstruct() { + auto renderPass = reinterpret_cast(0xbadbeef); + auto framebuffer = reinterpret_cast(0xdeadcafe); + + RenderPassBeginInfo info{renderPass, framebuffer, Range2Di{{3, 7}, {15, 78}}}; + CORRADE_COMPARE(info->renderPass, renderPass); + CORRADE_COMPARE(info->framebuffer, framebuffer); + CORRADE_COMPARE(Range2Di{info->renderArea}, (Range2Di{{3, 7}, {15, 78}})); + CORRADE_COMPARE(info->clearValueCount, 0); + CORRADE_VERIFY(!info->pClearValues); +} + +void RenderPassTest::beginInfoConstructNoInit() { + RenderPassBeginInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) RenderPassBeginInfo{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 RenderPassTest::beginInfoConstructClears() { + RenderPassBeginInfo info{{}, {}, {}}; + info.clearColor(5, Color4{0.5f, 0.6f, 0.7f, 0.8f}) + .clearColor(7, Vector4i{1, -2, 3, -4}) + .clearColor(15, Vector4ui{8, 12, 33, 1}) + /* Use one of the first IDs at last to verify the array doesn't get + shortened */ + .clearDepthStencil(2, 15.0f, 1337); + + CORRADE_COMPARE(info->clearValueCount, 16); + CORRADE_COMPARE(Color4{info->pClearValues[5].color}, (Color4{0.5f, 0.6f, 0.7f, 0.8f})); + CORRADE_COMPARE(Vector4i{info->pClearValues[7].color}, (Vector4i{1, -2, 3, -4})); + CORRADE_COMPARE(Vector4ui{info->pClearValues[15].color}, (Vector4ui{8, 12, 33, 1})); + CORRADE_COMPARE(info->pClearValues[2].depthStencil.depth, 15.0f); + CORRADE_COMPARE(info->pClearValues[2].depthStencil.stencil, 1337); +} + +void RenderPassTest::beginInfoConstructFromVk() { + VkRenderPassBeginInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + RenderPassBeginInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void RenderPassTest::beginInfoConstructCopy() { + CORRADE_VERIFY(!(std::is_copy_constructible{})); + CORRADE_VERIFY(!(std::is_copy_assignable{})); +} + +void RenderPassTest::beginInfoConstructMove() { + RenderPassBeginInfo a{{}, {}, {}}; + a.clearColor(5, Color4{0.5f, 0.6f, 0.7f, 0.8f}); + CORRADE_COMPARE(a->clearValueCount, 6); + CORRADE_COMPARE(Color4{a->pClearValues[5].color}, (Color4{0.5f, 0.6f, 0.7f, 0.8f})); + + RenderPassBeginInfo b = std::move(a); + CORRADE_COMPARE(a->clearValueCount, 0); + CORRADE_VERIFY(!a->pClearValues); + CORRADE_COMPARE(b->clearValueCount, 6); + CORRADE_COMPARE(Color4{b->pClearValues[5].color}, (Color4{0.5f, 0.6f, 0.7f, 0.8f})); + + RenderPassBeginInfo c{VkRenderPassBeginInfo{}}; + c = std::move(b); + CORRADE_COMPARE(b->clearValueCount, 0); + CORRADE_VERIFY(!b->pClearValues); + CORRADE_COMPARE(c->clearValueCount, 6); + CORRADE_COMPARE(Color4{c->pClearValues[5].color}, (Color4{0.5f, 0.6f, 0.7f, 0.8f})); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void RenderPassTest::subpassBeginInfoConstruct() { + SubpassBeginInfo info{SubpassContents::SecondaryCommandBuffers}; + CORRADE_COMPARE(info->contents, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); +} + +void RenderPassTest::subpassBeginInfoConstructNoInit() { + SubpassBeginInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) SubpassBeginInfo{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 RenderPassTest::subpassBeginInfoConstructFromVk() { + VkSubpassBeginInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + SubpassBeginInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void RenderPassTest::subpassEndInfoConstruct() { + SubpassEndInfo info{}; + CORRADE_VERIFY(info->sType); +} + +void RenderPassTest::subpassEndInfoConstructNoInit() { + SubpassEndInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) SubpassEndInfo{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 RenderPassTest::subpassEndInfoConstructFromVk() { + VkSubpassEndInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + SubpassEndInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::RenderPassTest) diff --git a/src/Magnum/Vk/Test/RenderPassVkTest.cpp b/src/Magnum/Vk/Test/RenderPassVkTest.cpp index 568405c59..dca80f064 100644 --- a/src/Magnum/Vk/Test/RenderPassVkTest.cpp +++ b/src/Magnum/Vk/Test/RenderPassVkTest.cpp @@ -27,7 +27,15 @@ #include #include +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Range.h" +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPoolCreateInfo.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/FramebufferCreateInfo.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/ImageCreateInfo.h" +#include "Magnum/Vk/ImageViewCreateInfo.h" #include "Magnum/Vk/RenderPassCreateInfo.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/VulkanTester.h" @@ -43,6 +51,8 @@ struct RenderPassVkTest: VulkanTester { void constructMove(); void wrap(); + + void cmdBeginEnd(); }; RenderPassVkTest::RenderPassVkTest() { @@ -51,7 +61,9 @@ RenderPassVkTest::RenderPassVkTest() { &RenderPassVkTest::constructSubpassNoAttachments, &RenderPassVkTest::constructMove, - &RenderPassVkTest::wrap}); + &RenderPassVkTest::wrap, + + &RenderPassVkTest::cmdBeginEnd}); } void RenderPassVkTest::construct() { @@ -129,6 +141,57 @@ void RenderPassVkTest::wrap() { device()->DestroyRenderPass(device(), renderPass, nullptr); } +void RenderPassVkTest::cmdBeginEnd() { + using namespace Math::Literals; + + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Using a depth attachment as well even though not strictly necessary to + catch potential unexpected bugs */ + Image color{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + Image depth{device(), ImageCreateInfo2D{ImageUsage::DepthStencilAttachment, + VK_FORMAT_D24_UNORM_S8_UINT, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + ImageView colorView{device(), ImageViewCreateInfo2D{color}}; + ImageView depthView{device(), ImageViewCreateInfo2D{depth}}; + + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + {color.format(), AttachmentLoadOperation::Clear, {}}, + {depth.format(), AttachmentLoadOperation::Clear, {}}, + }) + .addSubpass(SubpassDescription{} + .setColorAttachments({0}) + .setDepthStencilAttachment(1) + ) + /* Further subpasses with no attachments so we can test nextSubpass() + but don't need to specify subpass dependencies (which I have no idea + about yet) */ + .addSubpass(SubpassDescription{}) + .addSubpass(SubpassDescription{}) + }; + + Framebuffer framebuffer{device(), FramebufferCreateInfo{renderPass, { + colorView, + depthView + }, {256, 256}}}; + + cmd.begin() + .beginRenderPass(RenderPassBeginInfo{renderPass, framebuffer, {{}, {256, 256}}} + .clearColor(0, 0x1f1f1f_rgbf) + .clearDepthStencil(1, 1.0f, 0)) + .nextSubpass() + /* The above overload goes through a different code path than this */ + .nextSubpass(SubpassEndInfo{}) + .endRenderPass() + .end(); + + /* Err there's not really anything visible to verify */ + CORRADE_VERIFY(cmd.handle()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::RenderPassVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index c190c7c12..961c23a66 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -80,6 +80,7 @@ class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; class RenderPass; +class RenderPassBeginInfo; class RenderPassCreateInfo; /* AttachmentDescription, AttachmentReference, SubpassDescription, SubpassDependency are useful only to be passed directly to @@ -87,6 +88,8 @@ class RenderPassCreateInfo; enum class Result: Int; class Shader; class ShaderCreateInfo; +class SubpassBeginInfo; +class SubpassEndInfo; enum class Version: UnsignedInt; #endif