diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index c703c85ed..3184110df 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -42,6 +42,7 @@ #include "Magnum/Vk/Image.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Queue.h" +#include "Magnum/Vk/RenderPass.h" #include "Magnum/Vk/Shader.h" #include "MagnumExternal/Vulkan/flextVkGlobal.h" @@ -416,6 +417,57 @@ indices.bindMemory(memory, indicesOffset); /* [Memory-mapping] */ } +{ +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; +/* [RenderPass-usage] */ +Vk::RenderPass renderPass{device, Vk::RenderPassCreateInfo{} + .setAttachments({ + VK_FORMAT_R8G8B8A8_SRGB, + VK_FORMAT_D24_UNORM_S8_UINT + }) + .addSubpass(Vk::SubpassDescription{} + .setColorAttachments({0}) + .setDepthStencilAttachment(1) + ) +}; +/* [RenderPass-usage] */ +} + +{ +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; +/* [RenderPass-usage-load-store] */ +Vk::RenderPass renderPass{device, Vk::RenderPassCreateInfo{} + .setAttachments({ + {VK_FORMAT_R8G8B8A8_SRGB, Vk::AttachmentLoadOperation::Clear, {}}, + {VK_FORMAT_D24_UNORM_S8_UINT, Vk::AttachmentLoadOperation::Clear, {}}, + }) + DOXYGEN_IGNORE() +}; +/* [RenderPass-usage-load-store] */ +} + +{ +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; +/* [RenderPass-usage-layout] */ +Vk::RenderPass renderPass{device, Vk::RenderPassCreateInfo{} + .setAttachments({ + {VK_FORMAT_R8G8B8A8_SRGB, + Vk::AttachmentLoadOperation::Clear, {}, + Vk::ImageLayout::ColorAttachment, + Vk::ImageLayout::ColorAttachment}, + {VK_FORMAT_D24_UNORM_S8_UINT, + Vk::AttachmentLoadOperation::Clear, {}, + Vk::ImageLayout::DepthStencilAttachment, + Vk::ImageLayout::DepthStencilAttachment}, + }) + .addSubpass(Vk::SubpassDescription{} + .setColorAttachments({{0, Vk::ImageLayout::ColorAttachment}}) + .setDepthStencilAttachment({1, Vk::ImageLayout::ColorAttachment}) + ) +}; +/* [RenderPass-usage-layout] */ +} + { Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; /* [Shader-usage] */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 19c91e7d5..d355e1eba 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -142,7 +142,7 @@ Vulkan function | Matching API @fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | | @fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | | @fn_vk{CreateQueryPool}, \n @fn_vk{DestroyQueryPool} | | -@fn_vk{CreateRenderPass}, \n @fn_vk{CreateRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{DestroyRenderPass} | | +@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** | | @fn_vk{CreateSemaphore}, \n @fn_vk{DestroySemaphore} | | @@ -305,9 +305,9 @@ Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{AllocationCallbacks} | intentionally @ref vulkan-wrapping-host-allocation "not exposed" @type_vk{ApplicationInfo} | @ref InstanceCreateInfo -@type_vk{AttachmentDescription}, \n @type_vk{AttachmentDescription2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@type_vk{AttachmentDescription}, \n @type_vk{AttachmentDescription2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref AttachmentDescription @type_vk{AttachmentDescriptionStencilLayout} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{AttachmentReference}, \n @type_vk{AttachmentReference2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@type_vk{AttachmentReference}, \n @type_vk{AttachmentReference2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref AttachmentReference @type_vk{AttachmentReferenceStencilLayout} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @subsection vulkan-mapping-structures-b B @@ -584,7 +584,7 @@ Vulkan structure | Matching API @type_vk{Rect2D} | convertible from/to @ref Range2Di using @ref Magnum/Vk/Integration.h @type_vk{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** | | +@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** | | @type_vk{RenderPassInputAttachmentAspectCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @@ -617,8 +617,8 @@ Vulkan structure | Matching API @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{SubpassDependency}, \n @type_vk{SubpassDependency2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{SubpassDescription}, \n @type_vk{SubpassDescription2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@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** | | @type_vk{SubresourceLayout} | | diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 57f48b57c..a1173fd77 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} | | +@vk_extension{KHR,create_renderpass2} | only render pass creation @vk_extension{EXT,sampler_filter_minmax} | | @vk_extension{KHR,image_format_list} | | @vk_extension{EXT,descriptor_indexing} | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 0fcdc7016..4992c92af 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -48,7 +48,8 @@ set(MagnumVk_GracefulAssert_SRCS ExtensionProperties.cpp Image.cpp LayerProperties.cpp - Memory.cpp) + Memory.cpp + RenderPass.cpp) set(MagnumVk_HEADERS Assert.h @@ -67,6 +68,7 @@ set(MagnumVk_HEADERS LayerProperties.h Memory.h Queue.h + RenderPass.h Result.h Shader.h TypeTraits.h diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 1654df8ba..f6ea5c485 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -29,6 +29,7 @@ #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Image.h" +#include "Magnum/Vk/RenderPass.h" #include "Magnum/Vk/Version.h" namespace Magnum { namespace Vk { namespace Implementation { @@ -61,6 +62,14 @@ DeviceState::DeviceState(Device& device) { bindImageMemoryImplementation = &Image::bindMemoryImplementationDefault; bindBufferMemoryImplementation = &Buffer::bindMemoryImplementationDefault; } + + if(device.isVersionSupported(Version::Vk12)) { + createRenderPassImplementation = &RenderPass::createImplementation12; + } else if(device.isExtensionEnabled()) { + createRenderPassImplementation = &RenderPass::createImplementationKHR; + } else { + createRenderPassImplementation = &RenderPass::createImplementationDefault; + } } }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 473124cc2..1b9d6fb43 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -28,7 +28,13 @@ #include "Magnum/Vk/Vk.h" #include "Magnum/Vk/Vulkan.h" -namespace Magnum { namespace Vk { namespace Implementation { +namespace Magnum { namespace Vk { + +/* Declared here instead of in Vk.h because it makes no sense to store it + anywhere */ +class RenderPassCreateInfo; + +namespace Implementation { struct DeviceState { explicit DeviceState(Device& instance); @@ -39,6 +45,7 @@ struct DeviceState { 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*); }; }}} diff --git a/src/Magnum/Vk/RenderPass.cpp b/src/Magnum/Vk/RenderPass.cpp new file mode 100644 index 000000000..7590489a6 --- /dev/null +++ b/src/Magnum/Vk/RenderPass.cpp @@ -0,0 +1,785 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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. +*/ + +#include "RenderPass.h" + +#include +#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/Implementation/DeviceState.h" + +namespace Magnum { namespace Vk { + +AttachmentDescription::AttachmentDescription(const VkFormat format, const AttachmentLoadOperation loadOperation, const AttachmentStoreOperation storeOperation, const ImageLayout initialLayout, const ImageLayout finalLayout, const Int samples, const Flags flags): _description{} { + _description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + _description.flags = VkAttachmentDescriptionFlags(flags); + _description.format = format; + _description.samples = VkSampleCountFlagBits(samples); + _description.loadOp = VkAttachmentLoadOp(loadOperation); + _description.storeOp = VkAttachmentStoreOp(storeOperation); + _description.initialLayout = VkImageLayout(initialLayout); + _description.finalLayout = VkImageLayout(finalLayout); +} + +AttachmentDescription::AttachmentDescription(const VkFormat format, const AttachmentLoadOperation loadOperation, const AttachmentStoreOperation storeOperation, const Int samples, const Flags flags): AttachmentDescription{format, loadOperation, storeOperation, ImageLayout::General, ImageLayout::General, samples, flags} {} + +AttachmentDescription::AttachmentDescription(const VkFormat format, const std::pair depthStencilLoadOperation, const std::pair depthStencilStoreOperation, const ImageLayout initialLayout, const ImageLayout finalLayout, const Int samples, const Flags flags): _description{} { + _description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + _description.flags = VkAttachmentDescriptionFlags(flags); + _description.format = format; + _description.samples = VkSampleCountFlagBits(samples); + _description.loadOp = VkAttachmentLoadOp(depthStencilLoadOperation.first); + _description.storeOp = VkAttachmentStoreOp(depthStencilStoreOperation.first); + _description.stencilLoadOp = VkAttachmentLoadOp(depthStencilLoadOperation.second); + _description.stencilStoreOp = VkAttachmentStoreOp(depthStencilStoreOperation.second); + _description.initialLayout = VkImageLayout(initialLayout); + _description.finalLayout = VkImageLayout(finalLayout); +} + +AttachmentDescription::AttachmentDescription(const VkFormat format, const std::pair depthStencilLoadOperation, const std::pair depthStencilStoreOperation, const Int samples, const Flags flags): AttachmentDescription{format, depthStencilLoadOperation, depthStencilStoreOperation, ImageLayout::General, ImageLayout::General, samples, flags} {} + +AttachmentDescription::AttachmentDescription(NoInitT) noexcept {} + +AttachmentDescription::AttachmentDescription(const VkAttachmentDescription2& description): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _description(description) {} + +AttachmentDescription::AttachmentDescription(const VkAttachmentDescription& description): _description{ + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + nullptr, + description.flags, + description.format, + description.samples, + description.loadOp, + description.storeOp, + description.stencilLoadOp, + description.stencilStoreOp, + description.initialLayout, + description.finalLayout +} {} + +namespace { + +/* Used by RenderPassCreateInfo::vkRenderPassCreateInfo() as well */ +VkAttachmentDescription vkAttachmentDescription(const VkAttachmentDescription2& description) { + return { + description.flags, + description.format, + description.samples, + description.loadOp, + description.storeOp, + description.stencilLoadOp, + description.stencilStoreOp, + description.initialLayout, + description.finalLayout + }; +} + +} + +VkAttachmentDescription AttachmentDescription::vkAttachmentDescription() const { + return Vk::vkAttachmentDescription(_description); +} + +AttachmentReference::AttachmentReference(const UnsignedInt attachment, const ImageLayout layout): _reference{} { + _reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + _reference.attachment = attachment; + _reference.layout = VkImageLayout(layout); +} + +AttachmentReference::AttachmentReference(): _reference{} { + _reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2; + _reference.attachment = VK_ATTACHMENT_UNUSED; + _reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; +} + +AttachmentReference::AttachmentReference(NoInitT) noexcept {} + +AttachmentReference::AttachmentReference(const VkAttachmentReference2& reference): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _reference(reference) {} + +AttachmentReference::AttachmentReference(const VkAttachmentReference& reference): _reference{ + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + nullptr, + reference.attachment, + reference.layout, + 0 +} {} + +namespace { + +/* Used in SubpassDescription::vkSubpassDescription() as well */ +VkAttachmentReference vkAttachmentReference(const VkAttachmentReference2& reference) { + return { + reference.attachment, + reference.layout + }; +} + +} + +VkAttachmentReference AttachmentReference::vkAttachmentReference() const { + return Vk::vkAttachmentReference(_reference); +} + +struct SubpassDescription::State { + Containers::ArrayTuple inputAttachments, colorAttachments; + AttachmentReference depthStencilAttachment; + Containers::Array preserveAttachments; +}; + +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; +} + +SubpassDescription::SubpassDescription(NoInitT) noexcept {} + +SubpassDescription::SubpassDescription(const VkSubpassDescription2& description): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _description(description) {} + +SubpassDescription::SubpassDescription(const VkSubpassDescription& description): _description{ + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + nullptr, + description.flags, + description.pipelineBindPoint, + 0, + /* Input, color, resolve and depth/stencil attachment references set + below */ + 0, nullptr, + 0, nullptr, nullptr, + nullptr, + description.preserveAttachmentCount, + description.pPreserveAttachments +}, _state{Containers::InPlaceInit} { + /* Convert all attachment references to the "version 2" format */ + setInputAttachmentsInternal({description.pInputAttachments, description.inputAttachmentCount}); + setColorAttachmentsInternal({description.pColorAttachments, description.colorAttachmentCount}, {description.pResolveAttachments, description.pResolveAttachments ? description.colorAttachmentCount : 0}); + if(description.pDepthStencilAttachment) + setDepthStencilAttachment(AttachmentReference{*description.pDepthStencilAttachment}); +} + +SubpassDescription::~SubpassDescription() = default; + +SubpassDescription::SubpassDescription(SubpassDescription&& other) noexcept: + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _description(other._description), + _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._description.pNext = nullptr; + other._description.inputAttachmentCount = 0; + other._description.pInputAttachments = nullptr; + other._description.colorAttachmentCount = 0; + other._description.pColorAttachments = nullptr; + other._description.pResolveAttachments = nullptr; + other._description.pDepthStencilAttachment = nullptr; + other._description.preserveAttachmentCount = 0; + other._description.pPreserveAttachments = nullptr; +} + +SubpassDescription& SubpassDescription::operator=(SubpassDescription&& other) noexcept { + using std::swap; + swap(other._description, _description); + swap(other._state, _state); + return *this; +} + +template void SubpassDescription::setInputAttachmentsInternal(Containers::ArrayView attachments) { + if(!_state) _state.emplace(); + + Containers::ArrayView wrappers; + Containers::ArrayView vkAttachments2; + + _state->inputAttachments = Containers::ArrayTuple{ + {NoInit, attachments.size(), wrappers}, + {NoInit, attachments.size(), vkAttachments2} + }; + + for(std::size_t i = 0; i != attachments.size(); ++i) { + new(wrappers + i) AttachmentReference{attachments[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkAttachments2 + i) VkAttachmentReference2(wrappers[i]); + } + + _description.inputAttachmentCount = attachments.size(); + _description.pInputAttachments = vkAttachments2; +} + +SubpassDescription& SubpassDescription::setInputAttachments(Containers::ArrayView attachments) & { + setInputAttachmentsInternal(attachments); + return *this; +} + +SubpassDescription&& SubpassDescription::setInputAttachments(Containers::ArrayView attachments) && { + return std::move(setInputAttachments(attachments)); +} + +SubpassDescription& SubpassDescription::setInputAttachments(std::initializer_list attachments) & { + return setInputAttachments(Containers::arrayView(attachments)); +} + +SubpassDescription&& SubpassDescription::setInputAttachments(std::initializer_list attachments) && { + return std::move(setInputAttachments(attachments)); +} + +template void SubpassDescription::setColorAttachmentsInternal(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments) { + if(!_state) _state.emplace(); + + CORRADE_ASSERT(!resolveAttachments.size() || resolveAttachments.size() == attachments.size(), + "Vk::SubpassDescription::setColorAttachments(): resolve attachments expected to be either empty or have a size of" << attachments.size() << "but got" << resolveAttachments.size(), ); + + Containers::ArrayView wrappers, + resolveWrappers; + Containers::ArrayView vkAttachments2, + vkResolveAttachments2; + Containers::ArrayView vkAttachments, + vkResolveAttachments; + + _state->colorAttachments = Containers::ArrayTuple{ + {NoInit, attachments.size(), wrappers}, + {NoInit, resolveAttachments.size(), resolveWrappers}, + {NoInit, attachments.size(), vkAttachments2}, + {NoInit, resolveAttachments.size(), vkResolveAttachments2}, + {NoInit, attachments.size(), vkAttachments}, + {NoInit, resolveAttachments.size(), vkResolveAttachments} + }; + + for(std::size_t i = 0; i != attachments.size(); ++i) { + new(wrappers + i) AttachmentReference{attachments[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkAttachments2 + i) VkAttachmentReference2(*wrappers[i]); + new(vkAttachments + i) VkAttachmentReference{ + vkAttachments2[i].attachment, + vkAttachments2[i].layout + }; + } + + if(!resolveAttachments.empty()) for(std::size_t i = 0; i != attachments.size(); ++i) { + new(resolveWrappers + i) AttachmentReference{resolveAttachments[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkResolveAttachments2 + i) VkAttachmentReference2(*resolveWrappers[i]); + new(vkResolveAttachments + i) VkAttachmentReference{ + vkResolveAttachments2[i].attachment, + vkResolveAttachments2[i].layout + }; + } + + _description.colorAttachmentCount = attachments.size(); + _description.pColorAttachments = vkAttachments2; + _description.pResolveAttachments = resolveAttachments.empty() ? + nullptr : vkResolveAttachments2; +} + +SubpassDescription& SubpassDescription::setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments) & { + setColorAttachmentsInternal(attachments, resolveAttachments); + return *this; +} + +SubpassDescription&& SubpassDescription::setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments) && { + return std::move(setColorAttachments(attachments, resolveAttachments)); +} + +SubpassDescription& SubpassDescription::setColorAttachments(Containers::ArrayView attachments) & { + return setColorAttachments(attachments, {}); +} + +SubpassDescription&& SubpassDescription::setColorAttachments(Containers::ArrayView attachments) && { + return std::move(setColorAttachments(attachments)); +} + +SubpassDescription& SubpassDescription::setColorAttachments(std::initializer_list attachments, std::initializer_list resolveAttachments) & { + return setColorAttachments(Containers::arrayView(attachments), Containers::arrayView(resolveAttachments)); +} + +SubpassDescription&& SubpassDescription::setColorAttachments(std::initializer_list attachments, std::initializer_list resolveAttachments) && { + return std::move(setColorAttachments(attachments, resolveAttachments)); +} + +SubpassDescription& SubpassDescription::setDepthStencilAttachment(AttachmentReference attachment) & { + if(!_state) _state.emplace(); + + _state->depthStencilAttachment = attachment; + + _description.pDepthStencilAttachment = _state->depthStencilAttachment; + return *this; +} + +SubpassDescription&& SubpassDescription::setDepthStencilAttachment(AttachmentReference attachment) && { + return std::move(setDepthStencilAttachment(attachment)); +} + +SubpassDescription& SubpassDescription::setPreserveAttachments(Containers::Array&& attachments) & { + if(!_state) _state.emplace(); + + _state->preserveAttachments = std::move(attachments); + _description.preserveAttachmentCount = _state->preserveAttachments.size(); + _description.pPreserveAttachments = _state->preserveAttachments; + return *this; +} + +SubpassDescription&& SubpassDescription::setPreserveAttachments(Containers::Array&& attachments) && { + return std::move(setPreserveAttachments(std::move(attachments))); +} + +SubpassDescription& SubpassDescription::setPreserveAttachments(Containers::ArrayView attachments) & { + Containers::Array copy{NoInit, attachments.size()}; + Utility::copy(attachments, copy); + return setPreserveAttachments(std::move(copy)); +} + +SubpassDescription&& SubpassDescription::setPreserveAttachments(Containers::ArrayView attachments) && { + return std::move(setPreserveAttachments(attachments)); +} + +SubpassDescription& SubpassDescription::setPreserveAttachments(std::initializer_list attachments) & { + return setPreserveAttachments(Containers::arrayView(attachments)); +} + +SubpassDescription&& SubpassDescription::setPreserveAttachments(std::initializer_list attachments) && { + return std::move(setPreserveAttachments(attachments)); +} + +namespace { + +std::size_t vkSubpassDescriptionExtrasSize(const VkSubpassDescription2& description) { + return sizeof(VkAttachmentReference)*(description.inputAttachmentCount + + description.colorAttachmentCount*(description.pResolveAttachments ? 2 : 1) + + (description.pDepthStencilAttachment ? 1 : 0)); +} + +std::pair vkSubpassDescriptionExtrasInto(const VkSubpassDescription2& description, char* const out) { + /* Not using an array view nor arrayCast() because the output is not + guaranteed to be divisible by the structure size and we have nothing + else to do with the size either */ + const auto attachmentReferences = reinterpret_cast(out); + + /* Copy what can be copied, the pointers will be filled below from the + running offset */ + VkSubpassDescription description1{ + description.flags, + description.pipelineBindPoint, + description.inputAttachmentCount, nullptr, + description.colorAttachmentCount, nullptr, nullptr, + nullptr, + description.preserveAttachmentCount, + description.pPreserveAttachments + }; + + /* Save convverted attachment references to offsets inside the out view, + update the pointers in the description structure for everything that has + attachments */ + std::size_t offset = 0; + if(description.inputAttachmentCount) + description1.pInputAttachments = attachmentReferences + offset; + for(std::size_t i = 0; i != description.inputAttachmentCount; ++i) + attachmentReferences[offset++] = vkAttachmentReference(description.pInputAttachments[i]); + if(description.colorAttachmentCount) + description1.pColorAttachments = attachmentReferences + offset; + for(std::size_t i = 0; i != description.colorAttachmentCount; ++i) + attachmentReferences[offset++] = vkAttachmentReference(description.pColorAttachments[i]); + if(description.pResolveAttachments) { + description1.pResolveAttachments = attachmentReferences + offset; + for(std::size_t i = 0; i != description.colorAttachmentCount; ++i) + attachmentReferences[offset++] = vkAttachmentReference(description.pResolveAttachments[i]); + } + if(description.pDepthStencilAttachment) { + description1.pDepthStencilAttachment = attachmentReferences + offset; + attachmentReferences[offset++] = vkAttachmentReference(*description.pDepthStencilAttachment); + } + + return {description1, offset*sizeof(VkAttachmentReference)}; +} + +} + +Containers::Array SubpassDescription::vkSubpassDescription() const { + /* Allocate an array to fit VkSubpassDescription together with all + converted VkAttachmentReference instances it needs. Expect the default + deleter is used so we don't need to wrap some other below. */ + const std::size_t extrasSize = vkSubpassDescriptionExtrasSize(_description); + Containers::Array storage{Containers::NoInit, sizeof(VkSubpassDescription) + extrasSize}; + CORRADE_INTERNAL_ASSERT(!storage.deleter()); + + /* Fill it with data and return, faking a size of 1 and with a custom + deleter that correctly deletes as a char array again */ + std::pair out = vkSubpassDescriptionExtrasInto(_description, storage.suffix(sizeof(VkSubpassDescription))); + CORRADE_INTERNAL_ASSERT(out.second == extrasSize); + *reinterpret_cast(storage.data()) = out.first; + return Containers::Array{ + reinterpret_cast(storage.release()), 1, + [](VkSubpassDescription* data, std::size_t) { + delete[] reinterpret_cast(data); + } + }; +} + +SubpassDependency::SubpassDependency(NoInitT) noexcept {} + +SubpassDependency::SubpassDependency(const VkSubpassDependency2& dependency): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _dependency(dependency) {} + +SubpassDependency::SubpassDependency(const VkSubpassDependency& dependency): _dependency{ + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + nullptr, + dependency.srcSubpass, + dependency.dstSubpass, + dependency.srcStageMask, + dependency.dstStageMask, + dependency.srcAccessMask, + dependency.dstAccessMask, + dependency.dependencyFlags, + 0 +} {} + +namespace { + +/* Used by RenderPassCreateInfo::vkRenderPassCreateInfo() as well */ +VkSubpassDependency vkSubpassDependency(const VkSubpassDependency2& dependency) { + return { + dependency.srcSubpass, + dependency.dstSubpass, + dependency.srcStageMask, + dependency.dstStageMask, + dependency.srcAccessMask, + dependency.dstAccessMask, + dependency.dependencyFlags + }; +} + +} + +VkSubpassDependency SubpassDependency::vkSubpassDependency() const { + return Vk::vkSubpassDependency(_dependency); +} + +struct RenderPassCreateInfo::State { + Containers::ArrayTuple attachments; + Containers::Array subpasses; + Containers::Array vkSubpasses2; + Containers::ArrayTuple dependencies; +}; + +RenderPassCreateInfo::RenderPassCreateInfo(const Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2; + _info.flags = VkRenderPassCreateFlags(flags); +} + +RenderPassCreateInfo::RenderPassCreateInfo(NoInitT) noexcept {} + +RenderPassCreateInfo::RenderPassCreateInfo(const VkRenderPassCreateInfo2& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +RenderPassCreateInfo::RenderPassCreateInfo(const VkRenderPassCreateInfo& info): _info{ + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + info.pNext, /* See note in constructor */ + info.flags, + /* Attachment descriptions, subpass descriptions and subpass dependencies + are set below */ + 0, nullptr, + 0, nullptr, + 0, nullptr, + /* View masks aren't present in the "version 1" structure */ + 0, nullptr +} { + /* Create attachment descriptions in the "version 2" format */ + setAttachmentsInternal({info.pAttachments, info.attachmentCount}); + + /* Add subpass descriptions in the "version 1" format. Since this has to + be done one-by-one to allow moves of SubpassDescription, no special + internal API is needed. */ + for(std::size_t i = 0; i != info.subpassCount; ++i) + addSubpass(SubpassDescription{info.pSubpasses[i]}); + + /* Create subpass dependencies in the "version 2" format */ + setDependenciesInternal({info.pDependencies, info.dependencyCount}); +} + +RenderPassCreateInfo::RenderPassCreateInfo(RenderPassCreateInfo&& 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.attachmentCount = 0; + other._info.pAttachments = nullptr; + other._info.subpassCount = 0; + other._info.pSubpasses = nullptr; + other._info.dependencyCount = 0; + other._info.pDependencies = nullptr; + other._info.correlatedViewMaskCount = 0; + other._info.pCorrelatedViewMasks = nullptr; +} + +RenderPassCreateInfo::~RenderPassCreateInfo() = default; + +RenderPassCreateInfo& RenderPassCreateInfo::operator=(RenderPassCreateInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._state, _state); + return *this; +} + +template void RenderPassCreateInfo::setAttachmentsInternal(Containers::ArrayView attachments) { + if(!_state) _state.emplace(); + + Containers::ArrayView wrappers; + Containers::ArrayView vkAttachments2; + _state->attachments = Containers::ArrayTuple{ + {NoInit, attachments.size(), wrappers}, + {NoInit, attachments.size(), vkAttachments2} + }; + + for(std::size_t i = 0; i != attachments.size(); ++i) { + new(wrappers + i) AttachmentDescription{attachments[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkAttachments2 + i) VkAttachmentDescription2(wrappers[i]); + } + + _info.attachmentCount = attachments.size(); + _info.pAttachments = vkAttachments2; +} + +RenderPassCreateInfo& RenderPassCreateInfo::setAttachments(Containers::ArrayView attachments) { + setAttachmentsInternal(attachments); + return *this; +} + +RenderPassCreateInfo& RenderPassCreateInfo::setAttachments(std::initializer_list attachments) { + return setAttachments(Containers::arrayView(attachments)); +} + +RenderPassCreateInfo& RenderPassCreateInfo::addSubpass(SubpassDescription&& subpass) { + if(!_state) _state.emplace(); + + /* Unfortunately here we can't use an ArrayTuple as it can't grow, and + accepting an array view / initializer list would mean a deep copy, which + is even less acceptable. So two separate allocations it is. */ + arrayAppend(_state->subpasses, std::move(subpass)); + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + arrayAppend(_state->vkSubpasses2, VkSubpassDescription2(_state->subpasses.back())); + + /* The arrays might have been reallocated, reconnect the info structure + pointers */ + _info.subpassCount = _state->vkSubpasses2.size(); + _info.pSubpasses = _state->vkSubpasses2; + return *this; +} + +template void RenderPassCreateInfo::setDependenciesInternal(Containers::ArrayView dependencies) { + if(!_state) _state.emplace(); + + Containers::ArrayView wrappers; + Containers::ArrayView vkDependencies2; + _state->dependencies = Containers::ArrayTuple{ + {NoInit, dependencies.size(), wrappers}, + {NoInit, dependencies.size(), vkDependencies2} + }; + + for(std::size_t i = 0; i != dependencies.size(); ++i) { + new(wrappers + i) SubpassDependency{dependencies[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkDependencies2 + i) VkSubpassDependency2(wrappers[i]); + } + + _info.dependencyCount = dependencies.size(); + _info.pDependencies = vkDependencies2; +} + +RenderPassCreateInfo& RenderPassCreateInfo::setDependencies(Containers::ArrayView dependencies) { + setDependenciesInternal(dependencies); + return *this; +} + +RenderPassCreateInfo& RenderPassCreateInfo::setDependencies(std::initializer_list dependencies) { + return setDependencies(Containers::arrayView(dependencies)); +} + +Containers::Array RenderPassCreateInfo::vkRenderPassCreateInfo() const { + /* Check that all the structs we copy into the contiguous array have the + expected alignment requirements. Subpass descriptions have the largest + (on 64bit) due to the internal pointers, so they'll go first. */ + static_assert( + alignof(VkSubpassDescription) == sizeof(void*) && + alignof(VkAttachmentDescription) == 4 && + alignof(VkAttachmentReference) == 4 && + alignof(VkSubpassDependency) == 4, + "unexpected alignment of VkRenderPassCreateInfo substructures"); + + /* Calculate size of all "extras" */ + const std::size_t structuresSize = + sizeof(VkSubpassDescription)*_info.subpassCount + + sizeof(VkAttachmentDescription)*_info.attachmentCount + + sizeof(VkSubpassDependency)*_info.dependencyCount; + std::size_t extrasSize = 0; + for(std::size_t i = 0; i != _info.subpassCount; ++i) + extrasSize += vkSubpassDescriptionExtrasSize(_info.pSubpasses[i]); + + /* Allocate an array to fit VkRenderPassCreateInfo together with all extras + it needs. Expect the default deleter is used so we don't need to wrap + some other below. */ + Containers::Array storage{Containers::NoInit, sizeof(VkRenderPassCreateInfo) + structuresSize + extrasSize}; + CORRADE_INTERNAL_ASSERT(!storage.deleter()); + + /* Copy what can be copied for the output info struct. The pointers will be + filled below from the running offset and the struct will be put into the + storage array at the very end. */ + VkRenderPassCreateInfo info1{ + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + /* Right now (Vulkan 1:2.163) the set of allowed pNext structures in + "version 2" is a *subset* of what's allowed in "version 1", so + simply reusing them should be fine. */ + _info.pNext, + _info.flags, + _info.attachmentCount, nullptr, + _info.subpassCount, nullptr, + _info.dependencyCount, nullptr + }; + + /* Fill in the dynamically-sized subpass description structs with the + higher alignment requirements first, put the "extras" at the end */ + std::size_t offset = sizeof(VkRenderPassCreateInfo); + std::size_t extrasOffset = sizeof(VkRenderPassCreateInfo) + structuresSize; + info1.pSubpasses = reinterpret_cast(storage + offset); + for(std::size_t i = 0; i != _info.subpassCount; ++i) { + std::pair out = vkSubpassDescriptionExtrasInto(_info.pSubpasses[i], storage + extrasOffset); + *reinterpret_cast(storage + offset) = out.first; + offset += sizeof(VkSubpassDescription); + extrasOffset += out.second; + } + CORRADE_INTERNAL_ASSERT(extrasOffset == storage.size()); + + /* Attachment descriptions */ + info1.pAttachments = reinterpret_cast(storage + offset); + for(std::size_t i = 0; i != _info.attachmentCount; ++i) { + *reinterpret_cast(storage + offset) = vkAttachmentDescription(_info.pAttachments[i]); + offset += sizeof(VkAttachmentDescription); + } + + /* Subpass dependencies */ + info1.pDependencies = reinterpret_cast(storage + offset); + for(std::size_t i = 0; i != _info.dependencyCount; ++i) { + *reinterpret_cast(storage + offset) = vkSubpassDependency(_info.pDependencies[i]); + offset += sizeof(VkSubpassDependency); + } + + CORRADE_INTERNAL_ASSERT(offset == sizeof(VkRenderPassCreateInfo) + structuresSize); + + *reinterpret_cast(storage.data()) = info1; + return Containers::Array{ + reinterpret_cast(storage.release()), 1, + [](VkRenderPassCreateInfo* data, std::size_t) { + delete[] reinterpret_cast(data); + } + }; +} + +RenderPass RenderPass::wrap(Device& device, const VkRenderPass handle, const HandleFlags flags) { + RenderPass out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +RenderPass::RenderPass(Device& device, const RenderPassCreateInfo& info): + _device{&device}, + #ifdef CORRADE_GRACEFUL_ASSERT + _handle{}, /* Otherwise the destructor dies if we hit the subpass assert */ + #endif + _flags{HandleFlag::DestroyOnDestruction} +{ + CORRADE_ASSERT(info->subpassCount, + "Vk::RenderPass: needs to be created with at least one subpass", ); + + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(_device->state().createRenderPassImplementation(*_device, info, nullptr, &_handle)); +} + +RenderPass::RenderPass(NoCreateT): _device{}, _handle{} {} + +RenderPass::RenderPass(RenderPass&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +RenderPass::~RenderPass() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyRenderPass(*_device, _handle, nullptr); +} + +RenderPass& RenderPass::operator=(RenderPass&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkRenderPass RenderPass::release() { + const VkRenderPass handle = _handle; + _handle = {}; + return handle; +} + +VkResult RenderPass::createImplementationDefault(Device& device, const RenderPassCreateInfo& info, const VkAllocationCallbacks* const callbacks, VkRenderPass* const handle) { + return device->CreateRenderPass(device, info.vkRenderPassCreateInfo(), callbacks, handle); +} + +VkResult RenderPass::createImplementationKHR(Device& device, const RenderPassCreateInfo& info, const VkAllocationCallbacks* const callbacks, VkRenderPass* const handle) { + return device->CreateRenderPass2KHR(device, info, callbacks, handle); +} + +VkResult RenderPass::createImplementation12(Device& device, const RenderPassCreateInfo& info, const VkAllocationCallbacks* const callbacks, VkRenderPass* const handle) { + return device->CreateRenderPass2(device, info, callbacks, handle); +} + +}} diff --git a/src/Magnum/Vk/RenderPass.h b/src/Magnum/Vk/RenderPass.h new file mode 100644 index 000000000..298364c90 --- /dev/null +++ b/src/Magnum/Vk/RenderPass.h @@ -0,0 +1,1147 @@ +#ifndef Magnum_Vk_RenderPass_h +#define Magnum_Vk_RenderPass_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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::AttachmentDescription, @ref Magnum::Vk::AttachmentReference, @ref Magnum::Vk::SubpassDescription, @ref Magnum::Vk::SubpassDependency, @ref Magnum::Vk::RenderPassCreateInfo, @ref Magnum::Vk::RenderPass, enum @ref Magnum::Vk::AttachmentLoadOperation, @ref Magnum::Vk::AttachmentStoreOperation + */ + +#include +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Attachment load operation +@m_since_latest + +Wraps a @type_vk_keyword{AttachmentLoadOp}, specifies how previous contents of +an attached image within the render area are treated at the beginning of a +subpass. +@see @ref AttachmentStoreOperation, @ref AttachmentDescription +@m_enum_values_as_keywords +*/ +enum class AttachmentLoadOperation: Int { + /** + * Previous contents are preserved. This is the conservative default when + * using the @ref AttachmentDescription::AttachmentDescription(VkFormat, Int, Flags) + * constructor. + * + * This value is also guaranteed to be @cpp 0 @ce, which means you're + * encouraged to simply use @cpp {} @ce in function calls and elsewhere. + */ + Load = VK_ATTACHMENT_LOAD_OP_LOAD, + + /** + * Previous contents are cleared to a value specified when a render pass + * instance is begun. + * + * @m_class{m-note m-success} + * + * @par + * Compared to @ref AttachmentLoadOperation::Load, if you don't need + * the previous contents, this can avoid a potentially expensive + * memory load on certain architectures. + */ + Clear = VK_ATTACHMENT_LOAD_OP_CLEAR, + + /** + * Previous contents don't need to be preserved. + * + * @m_class{m-note m-success} + * + * @par + * Compared to @ref AttachmentLoadOperation::Load and + * @ref AttachmentLoadOperation::Clear, if you will be fully + * overwriting the contents anyway, this can avoid a potentially + * expensive memory load or clear operation. + */ + DontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE +}; + +/** +@brief Attachment load operation +@m_since_latest + +Wraps a @type_vk_keyword{AttachmentStoreOp}, specifies how contents of an +attached image generated during the render pass within the render area are +treated at the end of a subpass. +@see @ref AttachmentLoadOperation, @ref AttachmentDescription +@m_enum_values_as_keywords +*/ +enum class AttachmentStoreOperation: Int { + /** + * Generated contents are written to memory. This is the conservative + * default when using the @ref AttachmentDescription::AttachmentDescription(VkFormat, Int, Flags) + * constructor. + * + * This value is also guaranteed to be @cpp 0 @ce, which means you're + * encouraged to simply use @cpp {} @ce in function calls and elsewhere. + */ + Store = VK_ATTACHMENT_STORE_OP_STORE, + + /** + * Generated contentgs don't need to be preserved. + * + * @m_class{m-note m-success} + * + * @par + * Compared to @ref AttachmentStoreOperation::Store, if the attachment + * was only used temporarily during a subpass, this can avoid a + * potentially expensive memory store operation on certain + * architectures. + */ + DontCare = VK_ATTACHMENT_STORE_OP_DONT_CARE +}; + +/** +@brief Attachment description +@m_since_latest + +Wraps a @type_vk_keyword{AttachmentDescription2}. This class is subsequently +passed to a @ref RenderPass, see its documentation for a high-level usage +information. + +@section Vk-AttachmentDescription-compatibility Compatibility with VkAttachmentDescription + +While the class operates on the @type_vk{AttachmentDescription2} structure +that's new in Vulkan 1.2 or is provided by the +@vk_extension{KHR,create_renderpass2} extenstion, conversion from and to +@type_vk{AttachmentDescription} is provided to some extent --- you can create +an @ref AttachmentDescription from it, call various methods on the instance and +then get a @type_vk{AttachmentDescription} back again using +@ref vkAttachmentDescription(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{AttachmentDescription2} fields and then perform the conversion +instead of editing the resulting @type_vk{AttachmentDescription}. + +Please note that the conversion to @type_vk{AttachmentDescription} will ignore +all fields that are present only in @type_vk{AttachmentDescription2} and its +substructures --- in particular, the whole `pNext` pointer chain is omitted. +When performing the conversion it's your responsibility to ensure nothing +significant was in the fields that were left out. +*/ +class MAGNUM_VK_EXPORT AttachmentDescription { + public: + /** + * @brief Attachment description flag + * + * Wraps @type_vk_keyword{AttachmentDescriptionFlagBits}. + * @see @ref Flags, @ref AttachmentDescription() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** Aliases the same device memory as other attachments */ + MayAlias = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT + }; + + /** + * @brief Attachment description flags + * + * Type-safe wrapper for @type_vk_keyword{AttachmentDescriptionFlags}. + * @see @ref AttachmentDescription() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param format Image format + * @param loadOperation How previous attachment contents are + * treated at the beginning of a subpass + * @param storeOperation How previous attachment contents are + * treated at the beginning of a subpass + * @param initialLayout Initial image layout. Can only be + * @ref ImageLayout::General, @ref ImageLayout::ShaderReadOnly, + * @ref ImageLayout::TransferSource, + * @ref ImageLayout::TransferDestination, and + * @ref ImageLayout::ColorAttachment in case of a color @p format + * or @ref ImageLayout::DepthStencilAttachment in case of a + * depth/stencil @p format. + * @param finalLayout Final image layout. Can only be + * @ref ImageLayout::General, @ref ImageLayout::ShaderReadOnly, + * @ref ImageLayout::TransferSource, + * @ref ImageLayout::TransferDestination, and + * @ref ImageLayout::ColorAttachment in case of a color @p format + * or @ref ImageLayout::DepthStencilAttachment in case of a + * depth/stencil @p format. + * @param samples Sample count + * @param flags Attachment description flags + * + * The following @type_vk{AttachmentDescription} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `format` + * - `samples` + * - `loadOp` to @p loadOperation + * - `storeOp` to @p storeOperation + * - `initialLayout` + * - `finalLayout` + * + * See also @ref AttachmentDescription(VkFormat, std::pair, std::pair, ImageLayout, ImageLayout, Int, Flags) + * for a constructing a combined depth/stencil attachment description. + */ + /*implicit*/ AttachmentDescription(VkFormat format, AttachmentLoadOperation loadOperation, AttachmentStoreOperation storeOperation, ImageLayout initialLayout, ImageLayout finalLayout, Int samples = 1, Flags flags = {}); + + /** + * @brief Construct with implicit conservative layout + * + * Equivalent to calling @ref AttachmentDescription(VkFormat, AttachmentLoadOperation, AttachmentStoreOperation, ImageLayout, ImageLayout, Int, Flags) + * with both @p initialLayout and @p finalLayout set to + * @ref ImageLayout::General. + */ + /*implicit*/ AttachmentDescription(VkFormat format, AttachmentLoadOperation loadOperation, AttachmentStoreOperation storeOperation, Int samples = 1, Flags flags = {}); + + /** + * @brief Construct for a combined depth/stencil attachment + * @param format Image format + * @param depthStencilLoadOperation How previous depth and stencil + * attachment contents are treated at the beginning of a subpass + * @param depthStencilStoreOperation How generated depth and stencil + * attachment contents are treated at the end of a subpass + * @param initialLayout Initial image layout. Can only + * be @ref ImageLayout::General, @ref ImageLayout::ShaderReadOnly, + * @ref ImageLayout::TransferSource, + * @ref ImageLayout::TransferDestination, and + * @ref ImageLayout::DepthStencilAttachment + * @param finalLayout Final image layout. Can only be + * @ref ImageLayout::General, @ref ImageLayout::ShaderReadOnly, + * @ref ImageLayout::TransferSource, + * @ref ImageLayout::TransferDestination, and + * @ref ImageLayout::DepthStencilAttachment + * @param samples Sample count + * @param flags Attachment description flags + * + * The following @type_vk{AttachmentDescription} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `format` + * - `samples` + * - `loadOp` and `stencilLoadOp` to @p loadOperation + * - `storeOp` and `stencilStoreOp` to @p storeOperation + * - `initialLayout` + * - `finalLayout` + * + * @todo Implement @vk_extension{KHR,separate_depth_stencil_layouts} + * and provide a pair of layouts as well + */ + /*implicit*/ AttachmentDescription(VkFormat format, std::pair depthStencilLoadOperation, std::pair depthStencilStoreOperation, ImageLayout initialLayout, ImageLayout finalLayout, Int samples = 1, Flags flags = {}); + + /** + * @brief Construct for a combined depth/stencil attachment with implicit conservative layout + * + * Equivalent to calling @ref AttachmentDescription(VkFormat, std::pair, std::pair, ImageLayout, ImageLayout, Int, Flags) + * with both @p initialLayout and @p finalLayout set to + * @ref ImageLayout::General. + */ + /*implicit*/ AttachmentDescription(VkFormat format, std::pair depthStencilLoadOperation, std::pair depthStencilStoreOperation, Int samples = 1, Flags flags = {}); + + /** + * @brief Construct with implicit conservative load/store operation and layout + * + * Equivalent to calling @ref AttachmentDescription(VkFormat, std::pair, std::pair, ImageLayout, ImageLayout, Int, Flags) + * with @ref AttachmentLoadOperation::Load and + * @ref AttachmentStoreOperation::Store and both @p initialLayout and + * @p finalLayout set to @ref ImageLayout::General. + */ + /*implicit*/ AttachmentDescription(VkFormat format, Int samples = 1, Flags flags = {}): AttachmentDescription{format, AttachmentLoadOperation{}, AttachmentStoreOperation{}, samples, flags} {} + + /** + * @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 AttachmentDescription(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 AttachmentDescription(const VkAttachmentDescription2& description); + + /** + * @brief Construct from a @type_vk{AttachmentDescription} + * + * Compared to the above, fills the common subset of + * @type_vk{AttachmentDescription2}, sets `sType` and zero-fills + * `pNext`. + * @see @ref vkAttachmentDescription() + */ + explicit AttachmentDescription(const VkAttachmentDescription& description); + + /** @brief Underlying @type_vk{AttachmentDescription2} structure */ + VkAttachmentDescription2& operator*() { return _description; } + /** @overload */ + const VkAttachmentDescription2& operator*() const { return _description; } + /** @overload */ + VkAttachmentDescription2* operator->() { return &_description; } + /** @overload */ + const VkAttachmentDescription2* operator->() const { return &_description; } + /** @overload */ + operator const VkAttachmentDescription2*() const { return &_description; } + + /** + * @overload + * + * The class is implicitly convertible to a reference in addition to + * a pointer because the type is commonly used in arrays as well, which + * would be annoying to do with a pointer conversion. + */ + operator const VkAttachmentDescription2&() const { return _description; } + + /** + * @brief Corresponding @type_vk{AttachmentDescription} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support version 1.2 or the @vk_extension{KHR,create_renderpass2} + * extension. See @ref Vk-AttachmentDescription-compatibility for more + * information. + * @see @ref AttachmentDescription(const VkAttachmentDescription&), + * @ref AttachmentReference::vkAttachmentReference(), + * @ref SubpassDescription::vkSubpassDescription(), + * @ref SubpassDependency::vkSubpassDependency(), + * @ref RenderPassCreateInfo::vkRenderPassCreateInfo() + */ + VkAttachmentDescription vkAttachmentDescription() const; + + private: + VkAttachmentDescription2 _description; +}; + +CORRADE_ENUMSET_OPERATORS(AttachmentDescription::Flags) + +/** +@brief Attachment reference +@m_since_latest + +Wraps a @type_vk_keyword{AttachmentReference2}. Used to reference attachments +inside a @ref SubpassDescription, which is subsequently passed to a +@ref RenderPass. See its documentation for a high-level overview. + +@section Vk-AttachmentReference-compatibility Compatibility with VkAttachmentReference + +While the class operates on the @type_vk{AttachmentReference2} structure that's +new in Vulkan 1.2 or is provided by the @vk_extension{KHR,create_renderpass2} +extenstion, conversion from and to @type_vk{AttachmentReference} is provided to +some extent --- you can create an @ref AttachmentReference from it, call +various methods on the instance and then get a @type_vk{AttachmentReference} +back again using @ref vkAttachmentReference(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{AttachmentReference2} fields and then perform the conversion instead +of editing the resulting @type_vk{AttachmentReference}. + +Please note that the conversion to @type_vk{AttachmentReference} will ignore +all fields that are present only in @type_vk{AttachmentReference2} --- in +particular, the whole `pNext` pointer chain is omitted. When performing the +conversion it's your responsibility to ensure nothing significant was in the +fields that were left out. +*/ +class MAGNUM_VK_EXPORT AttachmentReference { + public: + /** + * @brief Constructor + * @param attachment Attachment index from the list passed to + * @ref RenderPassCreateInfo::setAttachments() + * @param layout Image layout. Should correspond to what's + * passed to @p initialLayout and @p finalLayout in + * @ref AttachmentDescription constructor + * + * The following @type_vk{AttachmentReference2} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `attachment` + * - `layout` + */ + /*implicit*/ AttachmentReference(UnsignedInt attachment, ImageLayout layout = + #ifdef DOXYGEN_GENERATING_OUTPUT + /* To avoid Image.h dependency */ + ImageLayout::General + #else + ImageLayout(VK_IMAGE_LAYOUT_GENERAL) + #endif + ); + + /** + * @brief Construct with no attachment + * + * The following @type_vk{AttachmentReference2} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `attachment` to @def_vk{ATTACHMENT_UNUSED} + * - `layout` to @ref ImageLayout::Undefined + */ + /*implicit*/ AttachmentReference(); + + /** + * @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 AttachmentReference(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 AttachmentReference(const VkAttachmentReference2& reference); + + /** + * @brief Construct from a @type_vk{AttachmentReference} + * + * Compared to the above, fills the common subset of + * @type_vk{AttachmentReference2}, sets `sType` and zero-fills `pNext` + * and `aspectMask`. + * @see @ref vkAttachmentReference() + */ + explicit AttachmentReference(const VkAttachmentReference& reference); + + /** @brief Underlying @type_vk{AttachmentReference2} structure */ + VkAttachmentReference2& operator*() { return _reference; } + /** @overload */ + const VkAttachmentReference2& operator*() const { return _reference; } + /** @overload */ + VkAttachmentReference2* operator->() { return &_reference; } + /** @overload */ + const VkAttachmentReference2* operator->() const { return &_reference; } + /** @overload */ + operator const VkAttachmentReference2*() const { return &_reference; } + + /** + * @overload + * + * The class is implicitly convertible to a reference in addition to + * a pointer because the type is commonly used in arrays as well, which + * would be annoying to do with a pointer conversion. + */ + operator const VkAttachmentReference2&() const { return _reference; } + + /** + * @brief Corresponding @type_vk{AttachmentReference} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support version 1.2 or the @vk_extension{KHR,create_renderpass2} + * extension. See @ref Vk-AttachmentReference-compatibility for more + * information. + * @see @ref AttachmentReference(const VkAttachmentReference&), + * @ref AttachmentDescription::vkAttachmentDescription(), + * @ref SubpassDescription::vkSubpassDescription(), + * @ref SubpassDependency::vkSubpassDependency(), + * @ref RenderPassCreateInfo::vkRenderPassCreateInfo() + */ + VkAttachmentReference vkAttachmentReference() const; + + private: + VkAttachmentReference2 _reference; +}; + +/** +@brief Subpass description +@m_since_latest + +Wraps a @type_vk_keyword{SubpassDescription2}. This class is subsequently +passed to a @ref RenderPass, see its documentation for a high-level usage +information. + +@section Vk-SubpassDescription-compatibility Compatibility with VkSubpassDescription + +While the class operates on the @type_vk{SubpassDescription2} structure that's +new in Vulkan 1.2 or is provided by the @vk_extension{KHR,create_renderpass2} +extenstion, conversion from and to @type_vk{SubpassDescription} is provided to +some extent --- you can create a @ref SubpassDescription from it, call various +methods on the instance and then get a @type_vk{SubpassDescription} back again +using @ref vkSubpassDescription(). Note that, because of the nested data +references, some internal pointers may still point to the originating instance, +so be sure to keep it in scope for as long as needed. + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{SubpassDescription2} fields and then perform the conversion instead of +editing the resulting @type_vk{SubpassDescription}. + +Please note that the conversion to @type_vk{SubpassDescription} will ignore all +fields that are present only in @type_vk{SubpassDescription2} and its +substructures --- in particular, the whole `pNext` pointer chain is omitted. +When performing the conversion it's your responsibility to ensure nothing +significant was in the fields that were left out. +*/ +class MAGNUM_VK_EXPORT SubpassDescription { + public: + /** + * @brief Subpass description flag + * + * Wraps @type_vk_keyword{SubpassDescriptionFlagBits}. + * @see @ref Flags, @ref SubpassDescription() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt {}; + + /** + * @brief Subpass description flags + * + * Type-safe wrapper for @type_vk_keyword{SubpassDescriptionFlags}. + * @see @ref SubpassDescription() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * + * The following @type_vk{SubpassDescription2} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `pipelineBindPoint` to @val_vk{PIPELINE_BIND_POINT_GRAPHICS,PipelineBindPoint} + * + * Use @ref setInputAttachments(), @ref setColorAttachments(), + * @ref setDepthStencilAttachment() and @ref setPreserveAttachments() + * to set attachments. Note that a subpass without any attachment is + * valid as well. + */ + explicit SubpassDescription(Flags flags = {}); + + /** + * @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 SubpassDescription(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 SubpassDescription(const VkSubpassDescription2& description); + + /** + * @brief Construct from a @type_vk{SubpassDescription} + * + * Compared to the above, fills the common subset of + * @type_vk{SubpassDescription2}, sets `sType` and zero-fills `pNext` + * and `viewMask`. + * @see @ref vkSubpassDescription() + */ + explicit SubpassDescription(const VkSubpassDescription& description); + + /** @brief Copying is not allowed */ + SubpassDescription(const SubpassDescription&) = delete; + + /** @brief Move constructor */ + SubpassDescription(SubpassDescription&&) noexcept; + + ~SubpassDescription(); + + /** @brief Copying is not allowed */ + SubpassDescription& operator=(const SubpassDescription&) = delete; + + /** @brief Move assignment */ + SubpassDescription& operator=(SubpassDescription&&) noexcept; + + /** + * @brief Set input attachments + * @return Reference to self (for method chaining) + */ + SubpassDescription& setInputAttachments(Containers::ArrayView attachments) &; + /** @overload */ + SubpassDescription&& setInputAttachments(Containers::ArrayView attachments) &&; + /** @overload */ + SubpassDescription& setInputAttachments(std::initializer_list attachments) &; + /** @overload */ + SubpassDescription&& setInputAttachments(std::initializer_list attachments) &&; + + /** + * @brief Set color attachments + * @return Reference to self (for method chaining) + * + * The @p resolveAttachments list is expected to be either empty or + * have the same size as @p attachments. If non-empty, each item has to + * have the same format as the corresponding item in @p attachments. + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + SubpassDescription& setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments = {}) &; + /** @overload */ + SubpassDescription&& setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments = {}) &&; + #else + /* So we don't need to include ArrayView */ + SubpassDescription& setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments) &; + SubpassDescription&& setColorAttachments(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments) &&; + SubpassDescription& setColorAttachments(Containers::ArrayView attachments) &; + SubpassDescription&& setColorAttachments(Containers::ArrayView attachments) &&; + #endif + /** @overload */ + SubpassDescription& setColorAttachments(std::initializer_list attachments, std::initializer_list resolveAttachments = {}) &; + /** @overload */ + SubpassDescription&& setColorAttachments(std::initializer_list attachments, std::initializer_list resolveAttachments = {}) &&; + + /** + * @brief Set depth/stencil attachment + * @return Reference to self (for method chaining) + * + * Calling this function with a default-constructed + * @ref AttachmentReference is equivalent to not calling it at all, and + * both mean there's no depth/stencil attachment. + */ + SubpassDescription& setDepthStencilAttachment(AttachmentReference attachment) &; + /** @overload */ + SubpassDescription&& setDepthStencilAttachment(AttachmentReference attachment) &&; + + /** + * @brief Set preserve attachments + * @return Reference to self (for method chaining) + * + * The @p attachment values are indices into the list passed to + * @ref RenderPassCreateInfo::setAttachments() + */ + SubpassDescription& setPreserveAttachments(Containers::ArrayView attachments) &; + /** @overload */ + SubpassDescription&& setPreserveAttachments(Containers::ArrayView attachments) &&; + /** @overload */ + SubpassDescription& setPreserveAttachments(Containers::Array&& attachments) &; + /** @overload */ + SubpassDescription&& setPreserveAttachments(Containers::Array&& attachments) &&; + /** @overload */ + SubpassDescription& setPreserveAttachments(std::initializer_list attachments) &; + /** @overload */ + SubpassDescription&& setPreserveAttachments(std::initializer_list attachments) &&; + + /** @brief Underlying @type_vk{SubpassDescription2} structure */ + VkSubpassDescription2& operator*() { return _description; } + /** @overload */ + const VkSubpassDescription2& operator*() const { return _description; } + /** @overload */ + VkSubpassDescription2* operator->() { return &_description; } + /** @overload */ + const VkSubpassDescription2* operator->() const { return &_description; } + /** @overload */ + operator const VkSubpassDescription2*() const { return &_description; } + + /** + * @overload + * + * The class is implicitly convertible to a reference in addition to + * a pointer because the type is commonly used in arrays as well, which + * would be annoying to do with a pointer conversion. + */ + operator const VkSubpassDescription2&() const { return _description; } + + /** + * @brief Corresponding @type_vk{SubpassDescription} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support version 1.2 or the @vk_extension{KHR,create_renderpass2} + * extension. Because the type references structures not present in + * @type_vk{SubpassDescription2}, it's returned wrapped it in a + * single-item array with the extra data appended at the end of the + * allocation. Note that, however, some internal pointers such as + * `pPreserveAttachments` may still point to the originating + * @ref SubpassDescription instance, the returned allocation is not + * completely standalone. See @ref Vk-SubpassDescription-compatibility + * for more information. + * @see @ref SubpassDescription(const VkSubpassDescription&), + * @ref AttachmentDescription::vkAttachmentDescription(), + * @ref AttachmentReference::vkAttachmentReference(), + * @ref SubpassDependency::vkSubpassDependency(), + * @ref RenderPassCreateInfo::vkRenderPassCreateInfo() + */ + Containers::Array vkSubpassDescription() const; + + private: + friend class RenderPassCreateInfo; + + template void setInputAttachmentsInternal(Containers::ArrayView attachments); + template void setColorAttachmentsInternal(Containers::ArrayView attachments, Containers::ArrayView resolveAttachments); + VkSubpassDescription2 _description; + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(SubpassDescription::Flags) + +/** +@brief Subpass dependency +@m_since_latest + +Wraps a @type_vk_keyword{SubpassDependency2}. This class is subsequently +passed to a @ref RenderPass, see its documentation for a high-level usage +information. + +@section Vk-SubpassDependency-compatibility Compatibility with VkSubpassDependency + +While the class operates on the @type_vk{SubpassDependency2} structure that's +new in Vulkan 1.2 or is provided by the @vk_extension{KHR,create_renderpass2} +extenstion, conversion from and to @type_vk{SubpassDependency} is provided to +some extent --- you can create a @ref SubpassDependency from it, call various +methods on the instance and then get a @type_vk{SubpassDependency} back again +using @ref vkSubpassDependency(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{SubpassDependency2} fields and then perform the conversion instead +of editing the resulting @type_vk{SubpassDependency}. + +Please note that the conversion to @type_vk{SubpassDependency} will ignore +all fields that are present only in @type_vk{SubpassDependency2} --- in +particular, the whole `pNext` pointer chain is omitted. When performing the +conversion it's your responsibility to ensure nothing significant was in the +fields that were left out. +*/ +class MAGNUM_VK_EXPORT SubpassDependency { + public: + /** + * @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 SubpassDependency(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 SubpassDependency(const VkSubpassDependency2& dependency); + + /** + * @brief Construct from a @type_vk{SubpassDependency} + * + * Compared to the above, fills the common subset of + * @type_vk{SubpassDescription2}, sets `sType` and zero-fills `pNext` + * and `viewOffset`. + * @see @ref vkSubpassDependency() + */ + explicit SubpassDependency(const VkSubpassDependency& dependency); + + /** @brief Underlying @type_vk{SubpassDependency2} structure */ + VkSubpassDependency2& operator*() { return _dependency; } + /** @overload */ + const VkSubpassDependency2& operator*() const { return _dependency; } + /** @overload */ + VkSubpassDependency2* operator->() { return &_dependency; } + /** @overload */ + const VkSubpassDependency2* operator->() const { return &_dependency; } + /** @overload */ + operator const VkSubpassDependency2*() const { return &_dependency; } + + /** + * @overload + * + * The class is implicitly convertible to a reference in addition to + * a pointer because the type is commonly used in arrays as well, which + * would be annoying to do with a pointer conversion. + */ + operator const VkSubpassDependency2&() const { return _dependency; } + + /** + * @brief Corresponding @type_vk{SubpassDependency} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support version 1.2 or the @vk_extension{KHR,create_renderpass2} + * extension. See @ref Vk-SubpassDependency-compatibility for more + * information. + * @see @ref SubpassDependency(const VkSubpassDependency&), + * @ref AttachmentDescription::vkAttachmentDescription(), + * @ref AttachmentReference::vkAttachmentReference(), + * @ref SubpassDescription::vkSubpassDescription(), + * @ref RenderPassCreateInfo::vkRenderPassCreateInfo() + */ + VkSubpassDependency vkSubpassDependency() const; + + private: + VkSubpassDependency2 _dependency; +}; + +/** +@brief Render pass creation info +@m_since_latest + +Wraps a @type_vk_keyword{RenderPassCreateInfo2} / +@type_vk_keyword{RenderPassCreateInfo}. See @ref RenderPass for usage +information. + +@section Vk-RenderPassCreateInfo-compatibility Compatibility with VkRenderPassCreateInfo + +While the class operates on the @type_vk{RenderPassCreateInfo2} structure +that's new in Vulkan 1.2 or is provided by the +@vk_extension{KHR,create_renderpass2} extenstion, conversion from and to +@type_vk{RenderPassCreateInfo} is provided to some extent ---- you can create a +@ref RenderPassCreateInfo from it, call various methods on the instance and +then get a @type_vk{RenderPassCreateInfo} back again using +@ref vkRenderPassCreateInfo(). Note that, because of the nested data +references, some internal pointers may still point to the originating instance, +so be sure to keep it in scope for as long as needed. + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{RenderPassCreateInfo2} fields and then perform the conversion instead +of editing the resulting @type_vk{RenderPassCreateInfo}. + +Please note that the conversion to @type_vk{RenderPassCreateInfo} will ignore +all fields that are present only in @type_vk{RenderPassCreateInfo2} and its +substructures --- in particular, `pCorrelatedViewMasks` are omitted. When +performing the conversion it's your responsibility to ensure nothing +significant was in the fields that were left out. +*/ +class MAGNUM_VK_EXPORT RenderPassCreateInfo { + public: + /** + * @brief Render pass creation flag + * + * Wraps @type_vk_keyword{RenderPassCreateFlagBits}. + * @see @ref Flags, @ref RenderPassCreateInfo() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt {}; + + /** + * @brief Render pass creation flags + * + * Type-safe wrapper for @type_vk_keyword{RenderPassCreateFlags}. + * @see @ref RenderPassCreateInfo() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param flags Render pass creation flags + * + * The following @type_vk{RenderPassCreateInfo2} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * + * You need to call @ref addSubpass() at least once for a valid setup. + */ + explicit RenderPassCreateInfo(Flags flags = {}); + + /** + * @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 RenderPassCreateInfo(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 RenderPassCreateInfo(const VkRenderPassCreateInfo2& info); + + /** + * @brief Construct from a @type_vk{RenderPassCreateInfo} + * + * Compared to the above, fills the common subset of + * @type_vk{RenderPassCreateInfo2}, sets `sType`, zero-fills + * `correlatedViewMaskCount` and `pCorrelatedViewMasks` and then calls + * @ref setAttachments(), @ref addSubpass() and @ref setDependencies() + * with instances created using + * @ref AttachmentDescription::AttachmentDescription(const VkAttachmentDescription&), + * @ref SubpassDescription::SubpassDescription(const VkSubpassDescription&) + * and @ref SubpassDependency::SubpassDependency(const VkSubpassDependency&). + * + * @attention The `pNext` member is currently taken as-is even though + * @type_vk{RenderPassCreateInfo2} accepts only a subset of the + * structure chain allowed by @type_vk{RenderPassCreateInfo}. This + * may change in the future, however now you have to take care to + * not list disallowed structures in the chain. + * + * @todo handle @type_vk{RenderPassInputAttachmentAspectCreateInfo} + * (@vk_extension{KHR,maintenance2}) and others here + * @see @ref vkRenderPassCreateInfo() + */ + explicit RenderPassCreateInfo(const VkRenderPassCreateInfo& info); + + /** @brief Copying is not allowed */ + RenderPassCreateInfo(const RenderPassCreateInfo&) = delete; + + /** @brief Move constructor */ + RenderPassCreateInfo(RenderPassCreateInfo&& other) noexcept; + + ~RenderPassCreateInfo(); + + /** @brief Copying is not allowed */ + RenderPassCreateInfo& operator=(const RenderPassCreateInfo&) = delete; + + /** @brief Move assignment */ + RenderPassCreateInfo& operator=(RenderPassCreateInfo&& other) noexcept; + + /** + * @brief Set attachments + * @return Reference to self (for method chaining) + * + * Subsequent calls to this function will *replace* the previous set, + * not append to it. + */ + RenderPassCreateInfo& setAttachments(Containers::ArrayView attachments); + /** @overload */ + RenderPassCreateInfo& setAttachments(std::initializer_list attachments); + + /** + * @brief Add a subpass + * @return Reference to self (for method chaining) + * + * At least one subpass has to be added. + * + * @m_class{m-note m-dim} + * + * @par + * Unlike @ref setAttachments() and @ref setDependencies(), due + * to nested allocations inside @ref SubpassDescription, it's more + * efficient to *move* the instances one by one than having to + * deep-copy a list. + */ + RenderPassCreateInfo& addSubpass(SubpassDescription&& subpass); + + /** + * @brief Set subpass dependencies + * @return Reference to self (for method chaining) + * + * ubsequent calls to this function will *replace* the previous set, + * not append to it. + */ + RenderPassCreateInfo& setDependencies(Containers::ArrayView dependencies); + /** @overload */ + RenderPassCreateInfo& setDependencies(std::initializer_list dependencies); + + /** @brief Underlying @type_vk{ShaderModuleCreateInfo} structure */ + VkRenderPassCreateInfo2& operator*() { return _info; } + /** @overload */ + const VkRenderPassCreateInfo2& operator*() const { return _info; } + /** @overload */ + VkRenderPassCreateInfo2* operator->() { return &_info; } + /** @overload */ + const VkRenderPassCreateInfo2* operator->() const { return &_info; } + /** @overload */ + operator const VkRenderPassCreateInfo2*() const { return &_info; } + + /** + * @brief Corresponding @type_vk{RenderPassCreateInfo} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support version 1.2 or the @vk_extension{KHR,create_renderpass2} + * extension. Because the type references structures not present in + * @type_vk{RenderPassCreateInfo2}, it's returned wrapped it in a + * single-item array with the extra data appended at the end of the + * allocation. Note that, however, some internal pointers such as + * `pNext` may still point to the originating @ref RenderPassCreateInfo + * instance, the returned allocation is not completely standalone. See + * @ref Vk-RenderPassCreateInfo-compatibility for more information. + * + * @attention The `pNext` member is currently taken as-is without + * converting the @type_vk{RenderPassCreateInfo2}-only fields to + * extra structures in the `pNext` chain for + * @type_vk{RenderPassCreateInfo}. This may change in the future, + * however now you have to take care to do needed modifications + * yourself afterwards. + * + * @see @ref RenderPassCreateInfo(const VkRenderPassCreateInfo&), + * @ref AttachmentDescription::vkAttachmentDescription(), + * @ref AttachmentReference::vkAttachmentReference(), + * @ref SubpassDescription::vkSubpassDescription(), + * @ref SubpassDependency::vkSubpassDependency() + */ + Containers::Array vkRenderPassCreateInfo() const; + + private: + template void setAttachmentsInternal(Containers::ArrayView attachments); + template void setDependenciesInternal(Containers::ArrayView dependencies); + + VkRenderPassCreateInfo2 _info; + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(RenderPassCreateInfo::Flags) + +namespace Implementation { struct DeviceState; } + +/** +@brief Render pass +@m_since_latest + +Wraps a @type_vk_keyword{RenderPass}, represents a collection of attachment +descriptions, subpasses and their dependencies. The render pass description is +independent of any specific image views used for attachments, these two are +connected together in a framebuffer. + +@section Vk-RenderPass-usage Basic usage + +A render pass is a set of attachments, described by @ref AttachmentDescription +instances, subpasses operating on those attachments, described by a +@ref SubpassDescription using @ref AttachmentReference instances, and subpass +dependencies, described by @ref SubpassDependency. + +A render pass has to have at least one subpass. It's common to have just one +subpass but while the subpass isn't required to operate on any attachments, +such case is rather rare. Following is a simple setup for one subpass operating +on a color and a combined depth/stencil attachment. The main parameter an +@ref AttachmentDescription needs is attachment format; the numbers passed to +@ref SubpassDescription::setColorAttachments() and +@ref SubpassDescription::setDepthStencilAttachment() are indices into the +@ref RenderPassCreateInfo::setAttachments() array, and it's actually +@ref AttachmentReference instances: + +@snippet MagnumVk.cpp RenderPass-usage + +The above again does a conservative estimate that you'd want to preserve the +attachment contents between render passes. Usually you'd want to clear the +framebuffer first instead of reusing its previous contents, which is done by +passing appropriate @ref AttachmentLoadOperation / +@ref AttachmentStoreOperation to the @ref AttachmentDescription constructor. +@ref AttachmentLoadOperation::Load and @ref AttachmentStoreOperation::Store are +conveniently the zero values, which means you can use @cpp {} @ce instead of +typing them out in full: + +@snippet MagnumVk.cpp RenderPass-usage-load-store + +Vulkan makes heavy use of image layouts for optimal memory access +and in all the cases above, @ref ImageLayout::General is used as an implicit +conservative layout. It's guaranteed to work for all device access, but it +might not always be optimal. A complete description of image layouts and their +use is out of scope of this reference, but for example, if the attached images +would be always only used as a render target, the above setup could be made +more optimal by explicitly specifying both a concrete initial and final layout +in the @ref AttachmentDescription constructors and in +each @link AttachmentReference @endlink: + +@snippet MagnumVk.cpp RenderPass-usage-layout +*/ +class MAGNUM_VK_EXPORT RenderPass { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the render pass is created on + * @param handle The @type_vk{RenderPass} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a render pass created using a constructor, the Vulkan render pass is + * by default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static RenderPass wrap(Device& device, VkRenderPass handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to create the render pass on + * @param info Render pass creation info + * + * @see @fn_vk_keyword{CreateRenderPass2}, + * @fn_vk_keyword{CreateRenderPass} + */ + explicit RenderPass(Device& device, const RenderPassCreateInfo& info); + + /** + * @brief Construct without creating the render pass + * + * 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 RenderPass(NoCreateT); + + /** @brief Copying is not allowed */ + RenderPass(const RenderPass&) = delete; + + /** @brief Move constructor */ + RenderPass(RenderPass&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{RenderPass} handle, unless the instance + * was created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyRenderPass}, @ref release() + */ + ~RenderPass(); + + /** @brief Copying is not allowed */ + RenderPass& operator=(const RenderPass&) = delete; + + /** @brief Move assignment */ + RenderPass& operator=(RenderPass&& other) noexcept; + + /** @brief Underlying @type_vk{RenderPass} handle */ + VkRenderPass handle() { return _handle; } + /** @overload */ + operator VkRenderPass() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan render pass + * + * Releases ownership of the Vulkan render pass and returns its handle + * so @fn_vk{DestroyRenderPass} is not called on destruction. The + * internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkRenderPass release(); + + private: + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static VkResult createImplementationDefault(Device&, const RenderPassCreateInfo&, const VkAllocationCallbacks*, VkRenderPass*); + MAGNUM_VK_LOCAL static VkResult createImplementationKHR(Device&, const RenderPassCreateInfo&, const VkAllocationCallbacks*, VkRenderPass*); + MAGNUM_VK_LOCAL static VkResult createImplementation12(Device&, const RenderPassCreateInfo&, const VkAllocationCallbacks*, VkRenderPass*); + + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkRenderPass _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index dd753028d..19c982022 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -39,6 +39,7 @@ corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkRenderPassTest RenderPassTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkShaderTest ShaderTest.cpp LIBRARIES MagnumVk) add_library(VkAssertTestObjects OBJECT AssertTest.cpp) @@ -115,6 +116,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + corrade_add_test(VkRenderPassVkTest RenderPassVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkShaderVkTest ShaderVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) target_include_directories(VkShaderVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/RenderPassTest.cpp b/src/Magnum/Vk/Test/RenderPassTest.cpp new file mode 100644 index 000000000..1f9518ef7 --- /dev/null +++ b/src/Magnum/Vk/Test/RenderPassTest.cpp @@ -0,0 +1,976 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/Image.h" +#include "Magnum/Vk/RenderPass.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct RenderPassTest: TestSuite::Tester { + explicit RenderPassTest(); + + /* While *ConstructFromVk() tests that going from VkFromThing -> Vk::Thing + -> VkToThing doesn't result in information loss, the *ConvertToVk() + tests additionally check that all calls both on our APIs and by editing + the contained structure are correctly propagated to the resulting + structures. */ + + void attachmentDescriptionConstruct(); + void attachmentDescriptionConstructImplicitLayout(); + void attachmentDescriptionConstructDepthStencil(); + void attachmentDescriptionConstructDepthStencilImplicitLayout(); + void attachmentDescriptionConstructImplicitLoadStoreLayout(); + void attachmentDescriptionConstructNoInit(); + template void attachmentDescriptionConstructFromVk(); + template void attachmentDescriptionConvertToVk(); + + void attachmentReferenceConstruct(); + void attachmentReferenceConstructUnused(); + void attachmentReferenceConstructNoInit(); + template void attachmentReferenceConstructFromVk(); + template void attachmentReferenceConvertToVk(); + + void subpassDescriptionConstruct(); + void subpassDescriptionConstructNoInit(); + void subpassDescriptionConstructInputAttachments(); + void subpassDescriptionConstructColorAttachments(); + void subpassDescriptionConstructColorResolveAttachments(); + void subpassDescriptionConstructColorResolveAttachmentsWrongCount(); + void subpassDescriptionConstructDepthStencilAttachment(); + void subpassDescriptionConstructPreserveAttachments(); + void subpassDescriptionConstructEmptyAttachmentLists(); + template void subpassDescriptionConstructFromVk(); + void subpassDescriptionConstructCopy(); + void subpassDescriptionConstructMove(); + template void subpassDescriptionConvertToVk(); + template void subpassDescriptionConvertToVkNoAttachments(); + template void subpassDescriptionConvertToVkNoResolveAttachments(); + void subpassDescriptionRvalue(); + + void subpassDependencyConstructNoInit(); + template void subpassDependencyConstructFromVk(); + template void subpassDependencyConvertToVk(); + + void createInfoConstruct(); + void createInfoConstructNoInit(); + void createInfoConstructAttachments(); + void createInfoConstructSubpasses(); + void createInfoConstructDependencies(); + template void createInfoConstructFromVk(); + void createInfoConstructCopy(); + void createInfoConstructMove(); + template void createInfoConvertToVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +RenderPassTest::RenderPassTest() { + addTests({&RenderPassTest::attachmentDescriptionConstruct, + &RenderPassTest::attachmentDescriptionConstructImplicitLayout, + &RenderPassTest::attachmentDescriptionConstructDepthStencil, + &RenderPassTest::attachmentDescriptionConstructDepthStencilImplicitLayout, + &RenderPassTest::attachmentDescriptionConstructImplicitLoadStoreLayout, + &RenderPassTest::attachmentDescriptionConstructNoInit, + &RenderPassTest::attachmentDescriptionConstructFromVk, + &RenderPassTest::attachmentDescriptionConstructFromVk, + &RenderPassTest::attachmentDescriptionConstructFromVk, + &RenderPassTest::attachmentDescriptionConstructFromVk, + &RenderPassTest::attachmentDescriptionConvertToVk, + &RenderPassTest::attachmentDescriptionConvertToVk, + + &RenderPassTest::attachmentReferenceConstruct, + &RenderPassTest::attachmentReferenceConstructUnused, + &RenderPassTest::attachmentReferenceConstructNoInit, + &RenderPassTest::attachmentReferenceConstructFromVk, + &RenderPassTest::attachmentReferenceConstructFromVk, + &RenderPassTest::attachmentReferenceConstructFromVk, + &RenderPassTest::attachmentReferenceConstructFromVk, + &RenderPassTest::attachmentReferenceConvertToVk, + &RenderPassTest::attachmentReferenceConvertToVk, + + &RenderPassTest::subpassDescriptionConstruct, + &RenderPassTest::subpassDescriptionConstructNoInit, + &RenderPassTest::subpassDescriptionConstructInputAttachments, + &RenderPassTest::subpassDescriptionConstructColorAttachments, + &RenderPassTest::subpassDescriptionConstructColorResolveAttachments, + &RenderPassTest::subpassDescriptionConstructColorResolveAttachmentsWrongCount, + &RenderPassTest::subpassDescriptionConstructDepthStencilAttachment, + &RenderPassTest::subpassDescriptionConstructPreserveAttachments, + &RenderPassTest::subpassDescriptionConstructEmptyAttachmentLists, + &RenderPassTest::subpassDescriptionConstructFromVk, + &RenderPassTest::subpassDescriptionConstructFromVk, + &RenderPassTest::subpassDescriptionConstructFromVk, + &RenderPassTest::subpassDescriptionConstructFromVk, + &RenderPassTest::subpassDescriptionConstructCopy, + &RenderPassTest::subpassDescriptionConstructMove, + &RenderPassTest::subpassDescriptionConvertToVk, + &RenderPassTest::subpassDescriptionConvertToVk, + &RenderPassTest::subpassDescriptionConvertToVkNoAttachments, + &RenderPassTest::subpassDescriptionConvertToVkNoAttachments, + &RenderPassTest::subpassDescriptionConvertToVkNoResolveAttachments, + &RenderPassTest::subpassDescriptionConvertToVkNoResolveAttachments, + &RenderPassTest::subpassDescriptionRvalue, + + &RenderPassTest::subpassDependencyConstructNoInit, + &RenderPassTest::subpassDependencyConstructFromVk, + &RenderPassTest::subpassDependencyConstructFromVk, + &RenderPassTest::subpassDependencyConstructFromVk, + &RenderPassTest::subpassDependencyConstructFromVk, + &RenderPassTest::subpassDependencyConvertToVk, + &RenderPassTest::subpassDependencyConvertToVk, + + &RenderPassTest::createInfoConstruct, + &RenderPassTest::createInfoConstructNoInit, + &RenderPassTest::createInfoConstructAttachments, + &RenderPassTest::createInfoConstructSubpasses, + &RenderPassTest::createInfoConstructDependencies, + &RenderPassTest::createInfoConstructFromVk, + &RenderPassTest::createInfoConstructFromVk, + &RenderPassTest::createInfoConstructFromVk, + &RenderPassTest::createInfoConstructFromVk, + &RenderPassTest::createInfoConstructCopy, + &RenderPassTest::createInfoConstructMove, + &RenderPassTest::createInfoConvertToVk, + &RenderPassTest::createInfoConvertToVk, + + &RenderPassTest::constructNoCreate, + &RenderPassTest::constructCopy}); +} + +template struct Traits; +#define _c(type) \ + template<> struct Traits { \ + static const char* name() { return #type; } \ + static Vk ## type convert(const type& instance) { \ + return instance.vk ## type (); \ + } \ + }; \ + template<> struct Traits { \ + static const char* name() { return #type "2"; } \ + static Vk ## type ## 2 convert(const type& instance) { \ + return instance; \ + } \ + }; +#define _ca(type) \ + template<> struct Traits { \ + static const char* name() { return #type; } \ + static Containers::Array convert(const type& instance) { \ + return instance.vk ## type (); \ + } \ + }; \ + template<> struct Traits { \ + static const char* name() { return #type "2"; } \ + /* So we have the same interface in both cases */ \ + static Containers::Array convert(const type& instance) { \ + return {Containers::InPlaceInit, {*instance}}; \ + } \ + }; +_c(AttachmentDescription) +_c(AttachmentReference) +_ca(SubpassDescription) +_c(SubpassDependency) +_ca(RenderPassCreateInfo) +#undef _c +#undef _ca + +void RenderPassTest::attachmentDescriptionConstruct() { + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + AttachmentLoadOperation::Clear, AttachmentStoreOperation::DontCare, + ImageLayout::ColorAttachment, ImageLayout::TransferDestination, + 4, AttachmentDescription::Flag::MayAlias}; + CORRADE_COMPARE(description->flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(description->format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(description->samples, VK_SAMPLE_COUNT_4_BIT); + CORRADE_COMPARE(description->loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(description->stencilLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + CORRADE_COMPARE(description->storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(description->stencilStoreOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->initialLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + CORRADE_COMPARE(description->finalLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); +} + +void RenderPassTest::attachmentDescriptionConstructImplicitLayout() { + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + AttachmentLoadOperation::Clear, AttachmentStoreOperation::DontCare, + 4, AttachmentDescription::Flag::MayAlias}; + CORRADE_COMPARE(description->flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(description->format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(description->samples, VK_SAMPLE_COUNT_4_BIT); + CORRADE_COMPARE(description->loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(description->stencilLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + CORRADE_COMPARE(description->storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(description->stencilStoreOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->initialLayout, VK_IMAGE_LAYOUT_GENERAL); + CORRADE_COMPARE(description->finalLayout, VK_IMAGE_LAYOUT_GENERAL); +} + +void RenderPassTest::attachmentDescriptionConstructDepthStencil() { + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + {AttachmentLoadOperation::Clear, AttachmentLoadOperation::DontCare}, + {AttachmentStoreOperation::Store, AttachmentStoreOperation::DontCare}, + ImageLayout::DepthStencilAttachment, ImageLayout::ShaderReadOnly, + 4, AttachmentDescription::Flag::MayAlias}; + CORRADE_COMPARE(description->flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(description->format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(description->samples, VK_SAMPLE_COUNT_4_BIT); + CORRADE_COMPARE(description->loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(description->stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + CORRADE_COMPARE(description->storeOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(description->initialLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + CORRADE_COMPARE(description->finalLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +void RenderPassTest::attachmentDescriptionConstructDepthStencilImplicitLayout() { + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + {AttachmentLoadOperation::Clear, AttachmentLoadOperation::DontCare}, + {AttachmentStoreOperation::Store, AttachmentStoreOperation::DontCare}, + 4, AttachmentDescription::Flag::MayAlias}; + CORRADE_COMPARE(description->flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(description->format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(description->samples, VK_SAMPLE_COUNT_4_BIT); + CORRADE_COMPARE(description->loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(description->stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + CORRADE_COMPARE(description->storeOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(description->initialLayout, VK_IMAGE_LAYOUT_GENERAL); + CORRADE_COMPARE(description->finalLayout, VK_IMAGE_LAYOUT_GENERAL); +} + +void RenderPassTest::attachmentDescriptionConstructImplicitLoadStoreLayout() { + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + 4, AttachmentDescription::Flag::MayAlias}; + CORRADE_COMPARE(description->flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(description->format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(description->samples, VK_SAMPLE_COUNT_4_BIT); + CORRADE_COMPARE(description->loadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + CORRADE_COMPARE(description->stencilLoadOp, VK_ATTACHMENT_LOAD_OP_LOAD); + CORRADE_COMPARE(description->storeOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->stencilStoreOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(description->initialLayout, VK_IMAGE_LAYOUT_GENERAL); + CORRADE_COMPARE(description->finalLayout, VK_IMAGE_LAYOUT_GENERAL); +} + +void RenderPassTest::attachmentDescriptionConstructNoInit() { + AttachmentDescription description{NoInit}; + description->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&description) AttachmentDescription{NoInit}; + CORRADE_COMPARE(description->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +template void RenderPassTest::attachmentDescriptionConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT; + from.format = VK_FORMAT_R8G8B8A8_SNORM; + from.samples = VK_SAMPLE_COUNT_32_BIT; + from.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + from.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + from.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + from.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + from.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL; + from.finalLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + AttachmentDescription description{from}; + To to = Traits::convert(description); + CORRADE_COMPARE(to.flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(to.format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(to.samples, VK_SAMPLE_COUNT_32_BIT); + CORRADE_COMPARE(to.loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(to.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + CORRADE_COMPARE(to.storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(to.stencilStoreOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(to.initialLayout, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.finalLayout, VK_IMAGE_LAYOUT_PREINITIALIZED); +} + +template void RenderPassTest::attachmentDescriptionConvertToVk() { + setTestCaseTemplateName(Traits::name()); + + AttachmentDescription description{VK_FORMAT_R8G8B8A8_SNORM, + {AttachmentLoadOperation::Clear, AttachmentLoadOperation::DontCare}, {AttachmentStoreOperation::Store, AttachmentStoreOperation::DontCare}, + ImageLayout::ShaderReadOnly, ImageLayout::TransferDestination, + 32, AttachmentDescription::Flag::MayAlias}; + + T out = Traits::convert(description); + CORRADE_COMPARE(out.flags, VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT); + CORRADE_COMPARE(out.format, VK_FORMAT_R8G8B8A8_SNORM); + CORRADE_COMPARE(out.samples, VK_SAMPLE_COUNT_32_BIT); + CORRADE_COMPARE(out.loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(out.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_DONT_CARE); + CORRADE_COMPARE(out.storeOp, VK_ATTACHMENT_STORE_OP_STORE); + CORRADE_COMPARE(out.stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(out.initialLayout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(out.finalLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); +} + +void RenderPassTest::attachmentReferenceConstruct() { + AttachmentReference reference{3, ImageLayout::ColorAttachment}; + CORRADE_COMPARE(reference->attachment, 3); + CORRADE_COMPARE(reference->layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); +} + +void RenderPassTest::attachmentReferenceConstructUnused() { + AttachmentReference reference; + CORRADE_COMPARE(reference->attachment, VK_ATTACHMENT_UNUSED); + CORRADE_COMPARE(reference->layout, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void RenderPassTest::attachmentReferenceConstructNoInit() { + AttachmentReference reference{NoInit}; + reference->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&reference) AttachmentReference{NoInit}; + CORRADE_COMPARE(reference->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +template void RenderPassTest::attachmentReferenceConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.attachment = 3; + from.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + AttachmentReference reference{from}; + To to = Traits::convert(reference); + CORRADE_COMPARE(to.attachment, 3); + CORRADE_COMPARE(to.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +template void RenderPassTest::attachmentReferenceConvertToVk() { + setTestCaseTemplateName(Traits::name()); + + AttachmentReference reference{3, ImageLayout::ShaderReadOnly}; + T out = Traits::convert(reference); + CORRADE_COMPARE(out.attachment, 3); + CORRADE_COMPARE(out.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +void RenderPassTest::subpassDescriptionConstruct() { + /** @todo use a real flag once it exists */ + SubpassDescription description{SubpassDescription::Flag(VK_INCOMPLETE)}; + CORRADE_COMPARE(description->flags, VK_INCOMPLETE); + CORRADE_COMPARE(description->inputAttachmentCount, 0); + CORRADE_VERIFY(!description->pInputAttachments); + CORRADE_COMPARE(description->colorAttachmentCount, 0); + CORRADE_VERIFY(!description->pColorAttachments); + CORRADE_VERIFY(!description->pResolveAttachments); + CORRADE_VERIFY(!description->pDepthStencilAttachment); + CORRADE_COMPARE(description->preserveAttachmentCount, 0); + CORRADE_VERIFY(!description->pPreserveAttachments); +} + +void RenderPassTest::subpassDescriptionConstructNoInit() { + SubpassDescription description{NoInit}; + description->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&description) SubpassDescription{NoInit}; + CORRADE_COMPARE(description->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void RenderPassTest::subpassDescriptionConstructInputAttachments() { + SubpassDescription description; + description.setInputAttachments({15, {}, 2}); + CORRADE_COMPARE(description->inputAttachmentCount, 3); + CORRADE_VERIFY(description->pInputAttachments); + CORRADE_COMPARE(description->pInputAttachments[0].attachment, 15); + CORRADE_COMPARE(description->pInputAttachments[1].attachment, VK_ATTACHMENT_UNUSED); + CORRADE_COMPARE(description->pInputAttachments[2].attachment, 2); +} + +void RenderPassTest::subpassDescriptionConstructColorAttachments() { + SubpassDescription description; + description.setColorAttachments({{}, 23}); + CORRADE_COMPARE(description->colorAttachmentCount, 2); + CORRADE_VERIFY(description->pColorAttachments); + CORRADE_VERIFY(!description->pResolveAttachments); + CORRADE_COMPARE(description->pColorAttachments[0].attachment, VK_ATTACHMENT_UNUSED); + CORRADE_COMPARE(description->pColorAttachments[1].attachment, 23); +} + +void RenderPassTest::subpassDescriptionConstructColorResolveAttachments() { + SubpassDescription description; + description.setColorAttachments({{}, 23}, {1, 0}); + + CORRADE_COMPARE(description->colorAttachmentCount, 2); + CORRADE_VERIFY(description->pColorAttachments); + CORRADE_COMPARE(description->pColorAttachments[0].attachment, VK_ATTACHMENT_UNUSED); + CORRADE_COMPARE(description->pColorAttachments[1].attachment, 23); + CORRADE_VERIFY(description->pResolveAttachments); + CORRADE_COMPARE(description->pResolveAttachments[0].attachment, 1); + CORRADE_COMPARE(description->pResolveAttachments[1].attachment, 0); +} + +void RenderPassTest::subpassDescriptionConstructColorResolveAttachmentsWrongCount() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + SubpassDescription description; + + std::ostringstream out; + Error redirectError{&out}; + description.setColorAttachments({0, 1}, {2, 3, 5}); + CORRADE_COMPARE(out.str(), + "Vk::SubpassDescription::setColorAttachments(): resolve attachments expected to be either empty or have a size of 2 but got 3\n"); +} + +void RenderPassTest::subpassDescriptionConstructDepthStencilAttachment() { + SubpassDescription description; + description.setDepthStencilAttachment(11); + CORRADE_VERIFY(description->pDepthStencilAttachment); + CORRADE_COMPARE(description->pDepthStencilAttachment->attachment, 11); +} + +void RenderPassTest::subpassDescriptionConstructPreserveAttachments() { + SubpassDescription description; + description.setPreserveAttachments({1, 0, 3, 14}); + CORRADE_COMPARE(description->preserveAttachmentCount, 4); + CORRADE_VERIFY(description->pPreserveAttachments); + CORRADE_COMPARE(description->pPreserveAttachments[0], 1); + CORRADE_COMPARE(description->pPreserveAttachments[1], 0); + CORRADE_COMPARE(description->pPreserveAttachments[2], 3); + CORRADE_COMPARE(description->pPreserveAttachments[3], 14); +} + +void RenderPassTest::subpassDescriptionConstructEmptyAttachmentLists() { + SubpassDescription description; + description.setColorAttachments({}, {}) + .setInputAttachments({}) + .setPreserveAttachments({}); + CORRADE_COMPARE(description->inputAttachmentCount, 0); + CORRADE_VERIFY(!description->pInputAttachments); + CORRADE_COMPARE(description->colorAttachmentCount, 0); + CORRADE_VERIFY(!description->pColorAttachments); + CORRADE_VERIFY(!description->pResolveAttachments); + CORRADE_COMPARE(description->preserveAttachmentCount, 0); + CORRADE_VERIFY(!description->pPreserveAttachments); +} + +template void RenderPassTest::subpassDescriptionConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + typedef typename std::remove_const::type>::type FromAttachmentReference; + + FromAttachmentReference inputAttachments[3]{}; + inputAttachments[0].attachment = 24; + inputAttachments[0].layout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL; + inputAttachments[1].attachment = 35; + inputAttachments[2].attachment = VK_ATTACHMENT_UNUSED; + + FromAttachmentReference colorAttachments[2]{}; + colorAttachments[0].attachment = 1; + colorAttachments[1].attachment = 3; + colorAttachments[1].layout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; + + FromAttachmentReference resolveAttachments[2]{}; + resolveAttachments[0].attachment = 25; + resolveAttachments[0].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + resolveAttachments[1].attachment = 12; + + FromAttachmentReference depthAttachment{}; + depthAttachment.attachment = 5; + depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL; + + UnsignedInt preserveAttachments[4]{0, 15, 23, 17}; + + From from{}; + from.flags = VK_NOT_READY; /* wrong, but to set at least something */ + from.pipelineBindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; + from.inputAttachmentCount = 3; + from.pInputAttachments = inputAttachments; + from.colorAttachmentCount = 2; + from.pColorAttachments = colorAttachments; + from.pResolveAttachments = resolveAttachments; + from.pDepthStencilAttachment = &depthAttachment; + from.preserveAttachmentCount = 4; + from.pPreserveAttachments = preserveAttachments; + + SubpassDescription description{from}; + Containers::Array array = Traits::convert(description); + const To& to = array[0]; + CORRADE_COMPARE(to.flags, VK_NOT_READY); /* wrong, but to set at least something */ + CORRADE_COMPARE(to.pipelineBindPoint, VK_PIPELINE_BIND_POINT_COMPUTE); + + CORRADE_COMPARE(to.inputAttachmentCount, 3); + CORRADE_VERIFY(to.pInputAttachments); + CORRADE_COMPARE(to.pInputAttachments[0].attachment, 24); + CORRADE_COMPARE(to.pInputAttachments[0].layout, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.pInputAttachments[1].attachment, 35); + CORRADE_COMPARE(to.pInputAttachments[2].attachment, VK_ATTACHMENT_UNUSED); + + CORRADE_COMPARE(to.colorAttachmentCount, 2); + CORRADE_VERIFY(to.pColorAttachments); + CORRADE_COMPARE(to.pColorAttachments[0].attachment, 1); + CORRADE_COMPARE(to.pColorAttachments[1].attachment, 3); + CORRADE_COMPARE(to.pColorAttachments[1].layout, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + CORRADE_VERIFY(to.pResolveAttachments); + CORRADE_COMPARE(to.pResolveAttachments[0].attachment, 25); + CORRADE_COMPARE(to.pResolveAttachments[0].layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.pResolveAttachments[1].attachment, 12); + + CORRADE_VERIFY(to.pDepthStencilAttachment); + CORRADE_COMPARE(to.pDepthStencilAttachment->attachment, 5); + CORRADE_COMPARE(to.pDepthStencilAttachment->layout, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL); + + CORRADE_COMPARE(to.preserveAttachmentCount, 4); + CORRADE_VERIFY(to.pPreserveAttachments); + CORRADE_COMPARE(to.pPreserveAttachments[0], 0); + CORRADE_COMPARE(to.pPreserveAttachments[1], 15); + CORRADE_COMPARE(to.pPreserveAttachments[2], 23); + CORRADE_COMPARE(to.pPreserveAttachments[3], 17); +} + +void RenderPassTest::subpassDescriptionConstructCopy() { + CORRADE_VERIFY(!(std::is_copy_constructible{})); + CORRADE_VERIFY(!(std::is_copy_assignable{})); +} + +void RenderPassTest::subpassDescriptionConstructMove() { + SubpassDescription a; + a.setInputAttachments({24, 35}); + CORRADE_COMPARE(a->inputAttachmentCount, 2); + CORRADE_COMPARE(a->pInputAttachments[1].attachment, 35); + + SubpassDescription b{std::move(a)}; + CORRADE_COMPARE(a->inputAttachmentCount, 0); + CORRADE_VERIFY(!a->pInputAttachments); + CORRADE_COMPARE(b->inputAttachmentCount, 2); + CORRADE_COMPARE(b->pInputAttachments[1].attachment, 35); + + SubpassDescription c; + c = std::move(b); + CORRADE_COMPARE(b->inputAttachmentCount, 0); + CORRADE_VERIFY(!b->pInputAttachments); + CORRADE_COMPARE(c->inputAttachmentCount, 2); + CORRADE_COMPARE(c->pInputAttachments[1].attachment, 35); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +template void RenderPassTest::subpassDescriptionConvertToVk() { + setTestCaseTemplateName(Traits::name()); + + SubpassDescription description{}; + description.setInputAttachments({ + 24, {35, ImageLayout::ShaderReadOnly}, 17 + }) + .setColorAttachments({1, 3}, {{25, ImageLayout::ColorAttachment}, 12}) + .setDepthStencilAttachment({5, ImageLayout::DepthStencilAttachment}) + .setPreserveAttachments({0, 15, 23, 17}); + + Containers::Array array = Traits::convert(description); + const T& to = array[0]; + CORRADE_COMPARE(to.inputAttachmentCount, 3); + CORRADE_VERIFY(to.pInputAttachments); + CORRADE_COMPARE(to.pInputAttachments[0].attachment, 24); + CORRADE_COMPARE(to.pInputAttachments[1].attachment, 35); + CORRADE_COMPARE(to.pInputAttachments[1].layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.pInputAttachments[2].attachment, 17); + + CORRADE_COMPARE(to.colorAttachmentCount, 2); + CORRADE_VERIFY(to.pColorAttachments); + CORRADE_COMPARE(to.pColorAttachments[0].attachment, 1); + CORRADE_COMPARE(to.pColorAttachments[1].attachment, 3); + CORRADE_VERIFY(to.pResolveAttachments); + CORRADE_COMPARE(to.pResolveAttachments[0].attachment, 25); + CORRADE_COMPARE(to.pResolveAttachments[0].layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + CORRADE_COMPARE(to.pResolveAttachments[1].attachment, 12); + + CORRADE_VERIFY(to.pDepthStencilAttachment); + CORRADE_COMPARE(to.pDepthStencilAttachment->attachment, 5); + CORRADE_COMPARE(to.pDepthStencilAttachment->layout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + CORRADE_COMPARE(to.preserveAttachmentCount, 4); + CORRADE_VERIFY(to.pPreserveAttachments); + CORRADE_COMPARE(to.pPreserveAttachments[0], 0); + CORRADE_COMPARE(to.pPreserveAttachments[1], 15); + CORRADE_COMPARE(to.pPreserveAttachments[2], 23); + CORRADE_COMPARE(to.pPreserveAttachments[3], 17); +} + +template void RenderPassTest::subpassDescriptionConvertToVkNoAttachments() { + setTestCaseTemplateName(Traits::name()); + + SubpassDescription description; + + Containers::Array array = Traits::convert(description); + const T& to = array[0]; + + CORRADE_COMPARE(to.inputAttachmentCount, 0); + CORRADE_VERIFY(!to.pInputAttachments); + + CORRADE_COMPARE(to.colorAttachmentCount, 0); + CORRADE_VERIFY(!to.pColorAttachments); + CORRADE_VERIFY(!to.pResolveAttachments); + + CORRADE_VERIFY(!to.pDepthStencilAttachment); + + CORRADE_COMPARE(to.preserveAttachmentCount, 0); + CORRADE_VERIFY(!to.pPreserveAttachments); +} + +template void RenderPassTest::subpassDescriptionConvertToVkNoResolveAttachments() { + setTestCaseTemplateName(Traits::name()); + + SubpassDescription description{}; + description.setColorAttachments({1, 3}); + + Containers::Array array = Traits::convert(description); + const T& to = array[0]; + CORRADE_COMPARE(to.colorAttachmentCount, 2); + CORRADE_VERIFY(to.pColorAttachments); + CORRADE_COMPARE(to.pColorAttachments[0].attachment, 1); + CORRADE_COMPARE(to.pColorAttachments[1].attachment, 3); + CORRADE_VERIFY(!to.pResolveAttachments); +} + +void RenderPassTest::subpassDescriptionRvalue() { + SubpassDescription&& description = SubpassDescription{} + .setInputAttachments(Containers::ArrayView{}) + .setInputAttachments(std::initializer_list{}) + .setColorAttachments(Containers::ArrayView{}) + .setColorAttachments(std::initializer_list{}) + .setColorAttachments(Containers::ArrayView{}, Containers::ArrayView{}) + .setColorAttachments(std::initializer_list{}, std::initializer_list{}) + .setDepthStencilAttachment({}) + .setPreserveAttachments(Containers::ArrayView{}) + .setPreserveAttachments(Containers::Array{}) + .setPreserveAttachments(std::initializer_list{}); + + /* Just to test something, main point is that the above compiles, links and + returns a &&. Can't test anything related to the contents because the + destructor gets called at the end of the expression. */ + CORRADE_VERIFY(&description); +} + +void RenderPassTest::subpassDependencyConstructNoInit() { + SubpassDependency dependency{NoInit}; + dependency->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&dependency) SubpassDependency{NoInit}; + CORRADE_COMPARE(dependency->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +template void RenderPassTest::subpassDependencyConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.srcSubpass = 3; + from.dstSubpass = 4; + from.srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + from.dstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + from.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + from.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + from.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + SubpassDependency dependency{from}; + To to = Traits::convert(dependency); + CORRADE_COMPARE(to.srcSubpass, 3); + CORRADE_COMPARE(to.dstSubpass, 4); + CORRADE_COMPARE(to.srcStageMask, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + CORRADE_COMPARE(to.dstStageMask, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); + CORRADE_COMPARE(to.srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); + CORRADE_COMPARE(to.dstAccessMask, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT); + CORRADE_COMPARE(to.dependencyFlags, VK_DEPENDENCY_BY_REGION_BIT); +} + +template void RenderPassTest::subpassDependencyConvertToVk() { + setTestCaseTemplateName(Traits::name()); + + CORRADE_SKIP("No SubpassDependency APIs to test."); +} + +void RenderPassTest::createInfoConstruct() { + /** @todo use a real flag once it exists */ + RenderPassCreateInfo info{RenderPassCreateInfo::Flag(VK_INCOMPLETE)}; + CORRADE_COMPARE(info->flags, VK_INCOMPLETE); + CORRADE_COMPARE(info->attachmentCount, 0); + CORRADE_VERIFY(!info->pAttachments); + CORRADE_COMPARE(info->subpassCount, 0); + CORRADE_VERIFY(!info->pSubpasses); + CORRADE_COMPARE(info->dependencyCount, 0); + CORRADE_VERIFY(!info->pDependencies); +} + +void RenderPassTest::createInfoConstructNoInit() { + RenderPassCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&info) RenderPassCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void RenderPassTest::createInfoConstructAttachments() { + RenderPassCreateInfo info; + info.setAttachments({ + {VK_FORMAT_R16G16B16A16_SFLOAT, AttachmentLoadOperation::Clear, AttachmentStoreOperation::DontCare}, + {VK_FORMAT_R8G8B8_SNORM, 4} + }); + CORRADE_COMPARE(info->attachmentCount, 2); + CORRADE_VERIFY(info->pAttachments); + CORRADE_COMPARE(info->pAttachments[0].format, VK_FORMAT_R16G16B16A16_SFLOAT); + CORRADE_COMPARE(info->pAttachments[0].loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR); + CORRADE_COMPARE(info->pAttachments[0].storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + CORRADE_COMPARE(info->pAttachments[1].format, VK_FORMAT_R8G8B8_SNORM); + CORRADE_COMPARE(info->pAttachments[1].samples, VK_SAMPLE_COUNT_4_BIT); +} + +void RenderPassTest::createInfoConstructSubpasses() { + RenderPassCreateInfo info; + info.addSubpass(SubpassDescription{} + .setColorAttachments({15, 34, 1}) + .setPreserveAttachments({22}) + ); + info.addSubpass(SubpassDescription{} + .setInputAttachments({17, {}}) + .setDepthStencilAttachment(1) + ); + CORRADE_COMPARE(info->subpassCount, 2); + CORRADE_VERIFY(info->pSubpasses); + CORRADE_COMPARE(info->pSubpasses[0].colorAttachmentCount, 3); + CORRADE_VERIFY(info->pSubpasses[0].pColorAttachments); + CORRADE_COMPARE(info->pSubpasses[0].pColorAttachments[1].attachment, 34); + CORRADE_COMPARE(info->pSubpasses[0].preserveAttachmentCount, 1); + CORRADE_VERIFY(info->pSubpasses[0].pPreserveAttachments); + CORRADE_COMPARE(info->pSubpasses[0].pPreserveAttachments[0], 22); + CORRADE_COMPARE(info->pSubpasses[1].inputAttachmentCount, 2); + CORRADE_VERIFY(info->pSubpasses[1].pInputAttachments); + CORRADE_COMPARE(info->pSubpasses[1].pInputAttachments[1].attachment, VK_ATTACHMENT_UNUSED); + CORRADE_VERIFY(info->pSubpasses[1].pDepthStencilAttachment); + CORRADE_COMPARE(info->pSubpasses[1].pDepthStencilAttachment->attachment, 1); +} + +void RenderPassTest::createInfoConstructDependencies() { + RenderPassCreateInfo info; + /** @todo update once we have a real API */ + VkSubpassDependency2 a{}; + a.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + VkSubpassDependency2 b{}; + b.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.setDependencies({ + SubpassDependency{a}, + SubpassDependency{b} + }); + CORRADE_COMPARE(info->dependencyCount, 2); + CORRADE_VERIFY(info->pDependencies); + CORRADE_COMPARE(info->pDependencies[0].sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + CORRADE_COMPARE(info->pDependencies[1].sType, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO); +} + +template void RenderPassTest::createInfoConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + VkRenderPassInputAttachmentAspectCreateInfo aspectInfo{}; + + typedef typename std::remove_const::type>::type FromAttachmentDescription; + typedef typename std::remove_const::type>::type FromSubpassDescription; + typedef typename std::remove_constpColorAttachments)>::type>::type FromAttachmentReference; + typedef typename std::remove_const::type>::type FromSubpassDependency; + + FromAttachmentDescription attachments[2]{}; + attachments[0].format = VK_FORMAT_A1R5G5B5_UNORM_PACK16; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + + FromAttachmentReference ref[2]{}; + ref[0].attachment = 1; + ref[1].attachment = 15; + ref[1].layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + UnsignedInt fiftyseven = 57; + + FromSubpassDescription subpasses[3]{}; + subpasses[0].colorAttachmentCount = 2; + subpasses[0].pColorAttachments = ref; + subpasses[1].pDepthStencilAttachment = &ref[1]; + subpasses[2].preserveAttachmentCount = 1; + subpasses[2].pPreserveAttachments = &fiftyseven; + + FromSubpassDependency dependency{}; + dependency.srcAccessMask = VK_ACCESS_INDEX_READ_BIT; + + From from{}; + from.pNext = &aspectInfo; + from.flags = VK_NOT_READY; /** @todo use a real flag once it exists */ + from.attachmentCount = 2; + from.pAttachments = attachments; + from.subpassCount = 3; + from.pSubpasses = subpasses; + from.dependencyCount = 1; + from.pDependencies = &dependency; + + RenderPassCreateInfo info{from}; + Containers::Array array = Traits::convert(info); + const To& to = array[0]; + CORRADE_COMPARE(to.pNext, &aspectInfo); + CORRADE_COMPARE(to.flags, VK_NOT_READY); + + CORRADE_COMPARE(to.attachmentCount, 2); + CORRADE_VERIFY(to.pAttachments); + CORRADE_COMPARE(to.pAttachments[0].format, VK_FORMAT_A1R5G5B5_UNORM_PACK16); + CORRADE_COMPARE(to.pAttachments[1].stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + + CORRADE_COMPARE(to.subpassCount, 3); + CORRADE_VERIFY(to.pSubpasses); + /* Test also that unset arrays stay 0/nullptr */ + CORRADE_COMPARE(to.pSubpasses[0].inputAttachmentCount, 0); + CORRADE_VERIFY(!to.pSubpasses[0].pInputAttachments); + CORRADE_COMPARE(to.pSubpasses[0].colorAttachmentCount, 2); + CORRADE_VERIFY(to.pSubpasses[0].pColorAttachments); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[0].attachment, 1); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[1].attachment, 15); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[1].layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_VERIFY(to.pSubpasses[1].pDepthStencilAttachment); + CORRADE_COMPARE(to.pSubpasses[1].pDepthStencilAttachment->attachment, 15); + CORRADE_COMPARE(to.pSubpasses[1].pDepthStencilAttachment->layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.pSubpasses[2].preserveAttachmentCount, 1); + CORRADE_VERIFY(to.pSubpasses[2].pPreserveAttachments); + CORRADE_COMPARE(to.pSubpasses[2].pPreserveAttachments[0], 57); + + CORRADE_COMPARE(to.dependencyCount, 1); + CORRADE_VERIFY(to.pDependencies); + CORRADE_COMPARE(to.pDependencies[0].srcAccessMask, VK_ACCESS_INDEX_READ_BIT); +} + +void RenderPassTest::createInfoConstructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +void RenderPassTest::createInfoConstructMove() { + RenderPassCreateInfo a; + a.setAttachments({VK_FORMAT_D32_SFLOAT, VK_FORMAT_R8G8B8_SNORM}); + CORRADE_COMPARE(a->attachmentCount, 2); + CORRADE_COMPARE(a->pAttachments[1].format, VK_FORMAT_R8G8B8_SNORM); + + RenderPassCreateInfo b{std::move(a)}; + CORRADE_COMPARE(a->attachmentCount, 0); + CORRADE_VERIFY(!a->pAttachments); + CORRADE_COMPARE(b->attachmentCount, 2); + CORRADE_COMPARE(b->pAttachments[1].format, VK_FORMAT_R8G8B8_SNORM); + + RenderPassCreateInfo c; + c = std::move(b); + CORRADE_COMPARE(b->attachmentCount, 0); + CORRADE_VERIFY(!b->pAttachments); + CORRADE_COMPARE(c->attachmentCount, 2); + CORRADE_COMPARE(c->pAttachments[1].format, VK_FORMAT_R8G8B8_SNORM); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +template void RenderPassTest::createInfoConvertToVk() { + VkSubpassDependency dependency{}; + dependency.srcAccessMask = VK_ACCESS_INDEX_READ_BIT; + + RenderPassCreateInfo info; + info.setAttachments({ + AttachmentDescription{VK_FORMAT_A1R5G5B5_UNORM_PACK16}, + AttachmentDescription{{}, {}, {AttachmentStoreOperation::Store, AttachmentStoreOperation::DontCare}} + }) + .addSubpass(SubpassDescription{}.setColorAttachments({1, {15, ImageLayout::ShaderReadOnly}})) + .addSubpass(SubpassDescription{}.setDepthStencilAttachment({15, ImageLayout::ShaderReadOnly})) + .addSubpass(SubpassDescription{}.setPreserveAttachments({57})) + .setDependencies({SubpassDependency{dependency}}); + Containers::Array array = Traits::convert(info); + const T& to = array[0]; + + CORRADE_COMPARE(to.attachmentCount, 2); + CORRADE_VERIFY(to.pAttachments); + CORRADE_COMPARE(to.pAttachments[0].format, VK_FORMAT_A1R5G5B5_UNORM_PACK16); + CORRADE_COMPARE(to.pAttachments[1].stencilStoreOp, VK_ATTACHMENT_STORE_OP_DONT_CARE); + + CORRADE_COMPARE(to.subpassCount, 3); + CORRADE_VERIFY(to.pSubpasses); + /* Test also that unset arrays stay 0/nullptr */ + CORRADE_COMPARE(to.pSubpasses[0].inputAttachmentCount, 0); + CORRADE_VERIFY(!to.pSubpasses[0].pInputAttachments); + CORRADE_COMPARE(to.pSubpasses[0].colorAttachmentCount, 2); + CORRADE_VERIFY(to.pSubpasses[0].pColorAttachments); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[0].attachment, 1); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[1].attachment, 15); + CORRADE_COMPARE(to.pSubpasses[0].pColorAttachments[1].layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_VERIFY(to.pSubpasses[1].pDepthStencilAttachment); + CORRADE_COMPARE(to.pSubpasses[1].pDepthStencilAttachment->attachment, 15); + CORRADE_COMPARE(to.pSubpasses[1].pDepthStencilAttachment->layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + CORRADE_COMPARE(to.pSubpasses[2].preserveAttachmentCount, 1); + CORRADE_VERIFY(to.pSubpasses[2].pPreserveAttachments); + CORRADE_COMPARE(to.pSubpasses[2].pPreserveAttachments[0], 57); + + CORRADE_COMPARE(to.dependencyCount, 1); + CORRADE_VERIFY(to.pDependencies); + CORRADE_COMPARE(to.pDependencies[0].srcAccessMask, VK_ACCESS_INDEX_READ_BIT); +} + +void RenderPassTest::constructNoCreate() { + { + RenderPass renderPass{NoCreate}; + CORRADE_VERIFY(!renderPass.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void RenderPassTest::constructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::RenderPassTest) diff --git a/src/Magnum/Vk/Test/RenderPassVkTest.cpp b/src/Magnum/Vk/Test/RenderPassVkTest.cpp new file mode 100644 index 000000000..03dc99648 --- /dev/null +++ b/src/Magnum/Vk/Test/RenderPassVkTest.cpp @@ -0,0 +1,134 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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. +*/ + +#include +#include +#include + +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/RenderPass.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct RenderPassVkTest: VulkanTester { + explicit RenderPassVkTest(); + + void construct(); + void constructNoSubpasses(); + void constructSubpassNoAttachments(); + void constructMove(); + + void wrap(); +}; + +RenderPassVkTest::RenderPassVkTest() { + addTests({&RenderPassVkTest::construct, + &RenderPassVkTest::constructNoSubpasses, + &RenderPassVkTest::constructSubpassNoAttachments, + &RenderPassVkTest::constructMove, + + &RenderPassVkTest::wrap}); +} + +void RenderPassVkTest::construct() { + { + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({VK_FORMAT_R8G8B8A8_SNORM}) + .addSubpass(SubpassDescription{}.setColorAttachments({0})) + }; + CORRADE_VERIFY(renderPass.handle()); + CORRADE_COMPARE(renderPass.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void RenderPassVkTest::constructNoSubpasses() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + RenderPass{device(), RenderPassCreateInfo{}}; + CORRADE_COMPARE(out.str(), "Vk::RenderPass: needs to be created with at least one subpass\n"); +} + +void RenderPassVkTest::constructSubpassNoAttachments() { + /* The spec requires at least one subpass, but it doesn't say anything + about attachments, so this should work */ + RenderPass renderPass{device(), RenderPassCreateInfo{} + .addSubpass(SubpassDescription{}) + }; + CORRADE_VERIFY(renderPass.handle()); +} + +void RenderPassVkTest::constructMove() { + RenderPass a{device(), RenderPassCreateInfo{} + .setAttachments({VK_FORMAT_R8G8B8A8_SNORM}) + .addSubpass(SubpassDescription{}.setColorAttachments({0})) + }; + VkRenderPass handle = a.handle(); + + RenderPass b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + RenderPass 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 RenderPassVkTest::wrap() { + VkRenderPass renderPass{}; + CORRADE_COMPARE(Result(device()->CreateRenderPass(device(), + RenderPassCreateInfo{} + .setAttachments({VK_FORMAT_R8G8B8A8_SNORM}) + .addSubpass(SubpassDescription{}.setColorAttachments({0})) + .vkRenderPassCreateInfo(), + nullptr, &renderPass)), Result::Success); + + auto wrapped = RenderPass::wrap(device(), renderPass, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), renderPass); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), renderPass); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyRenderPass(device(), renderPass, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::RenderPassVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 46621391b..75cca50ff 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -64,6 +64,7 @@ typedef Containers::EnumSet MemoryHeapFlags; class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; +class RenderPass; enum class Result: Int; enum class Version: UnsignedInt; #endif