diff --git a/CMakeLists.txt b/CMakeLists.txt index a9d2956..f89fe28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,19 +36,24 @@ add_executable(vk_expe src/core/utils.cpp src/core/Logger.cpp + src/vk/Wrapper.cpp src/vk/Context.cpp src/vk/CommandPool.cpp src/vk/CommandBuffer.cpp src/vk/Fence.cpp src/vk/Semaphore.cpp + src/vk/RenderPass.cpp src/vk/Framebuffer.cpp src/vk/ShaderModule.cpp src/vk/Pipeline.cpp src/vk/Memory.cpp src/vk/Buffer.cpp src/vk/ImageView.cpp + src/vk/DescriptorSetLayout.cpp + src/vk/PipelineLayout.cpp + src/vk/DescriptorPool.cpp + src/vk/DescriptorSet.cpp src/vk/Swapchain.cpp - src/vk/RenderPass.cpp src/main.cpp src/Simplex.cpp diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 223e6ef..f9624b5 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -187,7 +187,7 @@ void VulkanTutorial::shutdown() { m_command_pool.destroy(); m_index_buffer.destroy(); m_vertex_buffer.destroy(); - m_context.destroy_descriptor_set_layout(m_descriptor_set_layout); + m_descriptor_set_layout.destroy(); m_render_done.clear(); @@ -314,12 +314,12 @@ void VulkanTutorial::destroy_swapchain_objects() { m_uniform_buffers.clear(); m_uniform_buffer_memory.free(); - m_context.destroy_descriptor_pool(m_descriptor_pool); + m_descriptor_pool.destroy(); m_framebuffers.clear(); m_pipeline.destroy(); - m_context.destroy_pipeline_layout(m_pipeline_layout); + m_pipeline_layout.destroy(); m_render_pass.destroy(); } @@ -369,25 +369,16 @@ void VulkanTutorial::create_render_pass() { } void VulkanTutorial::create_descriptor_set_layout() { - VkDescriptorSetLayoutBinding binding { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, + VkDescriptorSetLayoutBinding binding[] { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, + } }; - VkDescriptorSetLayoutCreateInfo layout_info { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = 1, - .pBindings = &binding, - }; - if(vkCreateDescriptorSetLayout( - m_context.device(), - &layout_info, - nullptr, - &m_descriptor_set_layout - ) != VK_SUCCESS) - throw std::runtime_error("failed to create descriptor set layout"); + m_descriptor_set_layout = vk::DescriptorSetLayout(m_context, binding); } void VulkanTutorial::create_graphic_pipeline() { @@ -506,21 +497,10 @@ void VulkanTutorial::create_graphic_pipeline() { .blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f }, }; - VkPipelineLayoutCreateInfo layout_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 1, - .pSetLayouts = &m_descriptor_set_layout, - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, + VkDescriptorSetLayout set_layouts[] { + m_descriptor_set_layout, }; - - if(vkCreatePipelineLayout( - m_context.device(), - &layout_info, - nullptr, - &m_pipeline_layout - ) != VK_SUCCESS) - throw std::runtime_error("failed to create pipeline layout"); + m_pipeline_layout = vk::PipelineLayout(m_context, set_layouts); VkGraphicsPipelineCreateInfo pipeline_info { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, @@ -628,46 +608,24 @@ void VulkanTutorial::create_uniform_buffer() { } void VulkanTutorial::create_descriptor_pool() { - VkDescriptorPoolSize pool_size { - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = uint32_t(m_swapchain.image_count()), + VkDescriptorPoolSize pool_sizes[] { + { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = uint32_t(m_swapchain.image_count()), + } }; - VkDescriptorPoolCreateInfo pool_info { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = uint32_t(m_swapchain.image_count()), - .poolSizeCount = 1, - .pPoolSizes = &pool_size, - }; - if(vkCreateDescriptorPool( - m_context.device(), - &pool_info, - nullptr, - &m_descriptor_pool - ) != VK_SUCCESS) - throw std::runtime_error("failed to create descriptor pool"); + m_descriptor_pool = vk::DescriptorPool( + m_context, + uint32_t(m_swapchain.image_count()), + pool_sizes + ); } void VulkanTutorial::create_descriptor_sets() { - const std::vector layouts( - m_swapchain.image_count(), m_descriptor_set_layout); - - VkDescriptorSetAllocateInfo alloc_info { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = m_descriptor_pool, - .descriptorSetCount = uint32_t(layouts.size()), - .pSetLayouts = layouts.data(), - }; - - m_descriptor_sets.resize(m_swapchain.image_count()); - if(vkAllocateDescriptorSets( - m_context.device(), - &alloc_info, - m_descriptor_sets.data() - ) != VK_SUCCESS) - throw std::runtime_error("failed to allocate descriptor sets"); - + m_descriptor_sets.clear(); for(size_t index = 0; index < m_swapchain.image_count(); index += 1) { + m_descriptor_sets.emplace_back(m_context, m_descriptor_pool, m_descriptor_set_layout); VkDescriptorBufferInfo buffer_info { .buffer = m_uniform_buffers[index], .offset = 0, @@ -762,11 +720,14 @@ void VulkanTutorial::create_command_buffers() { VK_INDEX_TYPE_UINT32 ); + VkDescriptorSet descriptor_sets[] { + m_descriptor_sets[index], + }; vkCmdBindDescriptorSets( command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, - 0, 1, &m_descriptor_sets[index], + 0, 1, descriptor_sets, 0, nullptr ); diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index b0bd2bd..99e9e78 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -2,6 +2,10 @@ #pragma once #include +#include +#include +#include +#include #include #include #include @@ -76,8 +80,8 @@ private: VkQueue m_presentation_queue = nullptr; vk::RenderPass m_render_pass; - VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; - VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; + vk::DescriptorSetLayout m_descriptor_set_layout; + vk::PipelineLayout m_pipeline_layout; vk::Pipeline m_pipeline; std::vector m_framebuffers; vk::CommandPool m_command_pool; @@ -86,8 +90,8 @@ private: std::vector m_uniform_buffers; std::vector m_uniform_buffer_offsets; vk::MemoryBlock m_uniform_buffer_memory; - VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; - std::vector m_descriptor_sets; + vk::DescriptorPool m_descriptor_pool; + std::vector m_descriptor_sets; std::vector m_command_buffers; std::vector m_render_done; diff --git a/src/vk/DescriptorPool.cpp b/src/vk/DescriptorPool.cpp new file mode 100644 index 0000000..ae5dc99 --- /dev/null +++ b/src/vk/DescriptorPool.cpp @@ -0,0 +1,74 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +DescriptorPool::DescriptorPool() noexcept { +} + +DescriptorPool::DescriptorPool( + Context& context, + uint32_t max_sets, + Array pool_sizes, + VkDescriptorPoolCreateFlags flags +) + : m_context(&context) +{ + assert(m_context); + + VkDescriptorPoolCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = flags, + .maxSets = max_sets, + .poolSizeCount = uint32_t(pool_sizes.size()), + .pPoolSizes = pool_sizes.data(), + }; + if(vkCreateDescriptorPool( + context.device(), + &create_info, + nullptr, + &m_descriptor_pool + ) != VK_SUCCESS) + throw std::runtime_error("failed to create descriptor pool"); +} + +DescriptorPool::DescriptorPool(DescriptorPool&& other) noexcept +{ + swap(*this, other); +} + +DescriptorPool::~DescriptorPool() noexcept { + if(!is_null()) + destroy(); +} + + +DescriptorPool& DescriptorPool::operator=(DescriptorPool&& other) noexcept { + swap(*this, other); + if(other) + other.destroy(); + return *this; +} + + +void DescriptorPool::destroy() noexcept { + assert(!is_null()); + assert(m_context); + + vkDestroyDescriptorPool( + m_context->device(), + m_descriptor_pool, + nullptr + ); + + m_descriptor_pool = nullptr; +} + + +} diff --git a/src/vk/DescriptorPool.h b/src/vk/DescriptorPool.h new file mode 100644 index 0000000..ea8d939 --- /dev/null +++ b/src/vk/DescriptorPool.h @@ -0,0 +1,68 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + +#include + + +namespace vk { + + +class DescriptorPool { +public: + DescriptorPool() noexcept; + DescriptorPool( + Context& context, + uint32_t max_sets, + Array pool_sizes, + VkDescriptorPoolCreateFlags flags=0 + ); + DescriptorPool(const DescriptorPool&) = delete; + DescriptorPool(DescriptorPool&& other) noexcept; + ~DescriptorPool() noexcept; + + DescriptorPool& operator=(const DescriptorPool&) = delete; + DescriptorPool& operator=(DescriptorPool&& other) noexcept; + + explicit inline operator bool() const noexcept { + return !is_null(); + } + + inline bool is_null() const noexcept { + return m_descriptor_pool == VK_NULL_HANDLE; + } + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline operator VkDescriptorPool() noexcept { + return m_descriptor_pool; + } + + inline VkDescriptorPool descriptor_pool() noexcept { + return m_descriptor_pool; + } + + friend inline void swap(DescriptorPool& descriptor_pool_0, DescriptorPool& descriptor_pool_1) noexcept { + using std::swap; + swap(descriptor_pool_0.m_context, descriptor_pool_1.m_context); + swap(descriptor_pool_0.m_descriptor_pool, descriptor_pool_1.m_descriptor_pool); + } + + void destroy() noexcept; + +private: + Context* m_context = nullptr; + VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; +}; + + +} diff --git a/src/vk/DescriptorSet.cpp b/src/vk/DescriptorSet.cpp new file mode 100644 index 0000000..c9c7677 --- /dev/null +++ b/src/vk/DescriptorSet.cpp @@ -0,0 +1,76 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +DescriptorSet::DescriptorSet() noexcept { +} + +DescriptorSet::DescriptorSet( + Context& context, + VkDescriptorPool descriptor_pool, + const VkDescriptorSetLayout& set_layout +) + : Wrapper(context, 0) + , m_descriptor_pool(descriptor_pool) +{ + assert(m_descriptor_pool != VK_NULL_HANDLE); + + VkDescriptorSetAllocateInfo create_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = m_descriptor_pool, + .descriptorSetCount = 1, + .pSetLayouts = &set_layout, + }; + if(vkAllocateDescriptorSets( + context.device(), + &create_info, + &m_descriptor_set + ) != VK_SUCCESS) + throw std::runtime_error("failed to create descriptor_set"); +} + +DescriptorSet::DescriptorSet(DescriptorSet&& other) noexcept +{ + swap(other); +} + +DescriptorSet::~DescriptorSet() noexcept { + if(!is_null()) + destroy(); +} + + +DescriptorSet& DescriptorSet::operator=(DescriptorSet&& other) noexcept { + swap(other); + if(other) + other.destroy(); + return *this; +} + + +void DescriptorSet::destroy() noexcept { + assert(!is_null()); + assert(m_context); + + if (!own_underlying_object()) + return; + + vkFreeDescriptorSets( + m_context->device(), + m_descriptor_pool, + 1, + &m_descriptor_set + ); + + m_descriptor_set = nullptr; +} + + +} diff --git a/src/vk/DescriptorSet.h b/src/vk/DescriptorSet.h new file mode 100644 index 0000000..cc7ed4b --- /dev/null +++ b/src/vk/DescriptorSet.h @@ -0,0 +1,79 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include +#include + +#include + + +namespace vk { + + +class DescriptorSet: public Wrapper { +public: + DescriptorSet() noexcept; + DescriptorSet( + Context& context, + VkDescriptorPool descriptor_pool, + const VkDescriptorSetLayout& set_layout + ); + DescriptorSet(const DescriptorSet&) = delete; + DescriptorSet(DescriptorSet&& other) noexcept; + ~DescriptorSet() noexcept; + + DescriptorSet& operator=(const DescriptorSet&) = delete; + DescriptorSet& operator=(DescriptorSet&& other) noexcept; + + explicit inline operator bool() const noexcept { + return !is_null(); + } + + inline bool is_null() const noexcept { + return m_descriptor_set == VK_NULL_HANDLE; + } + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline const VkDescriptorPool descriptor_pool() const noexcept { + return m_descriptor_pool; + } + + inline VkDescriptorPool descriptor_pool() noexcept { + return m_descriptor_pool; + } + + inline operator VkDescriptorSet() noexcept { + return m_descriptor_set; + } + + inline VkDescriptorSet descriptor_set() noexcept { + return m_descriptor_set; + } + + inline void swap(DescriptorSet& other) noexcept { + using std::swap; + swap(m_context, other.m_context); + swap(m_descriptor_pool, other.m_descriptor_pool); + swap(m_descriptor_set, other.m_descriptor_set); + } + + friend inline void swap(DescriptorSet& descriptor_set_0, DescriptorSet& descriptor_set_1) noexcept { + descriptor_set_0.swap(descriptor_set_1); + } + + void destroy() noexcept; + +private: + VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE; + VkDescriptorSet m_descriptor_set = VK_NULL_HANDLE; +}; + + +} diff --git a/src/vk/DescriptorSetLayout.cpp b/src/vk/DescriptorSetLayout.cpp new file mode 100644 index 0000000..51e1c78 --- /dev/null +++ b/src/vk/DescriptorSetLayout.cpp @@ -0,0 +1,67 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +DescriptorSetLayout::DescriptorSetLayout() noexcept { +} + +DescriptorSetLayout::DescriptorSetLayout(Context& context, Array bindings) + : m_context(&context) +{ + assert(m_context); + + VkDescriptorSetLayoutCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = uint32_t(bindings.size()), + .pBindings = bindings.data(), + }; + if(vkCreateDescriptorSetLayout( + context.device(), + &create_info, + nullptr, + &m_descriptor_set_layout + ) != VK_SUCCESS) + throw std::runtime_error("failed to create descriptor set layout"); +} + +DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept +{ + swap(*this, other); +} + +DescriptorSetLayout::~DescriptorSetLayout() noexcept { + if(!is_null()) + destroy(); +} + + +DescriptorSetLayout& DescriptorSetLayout::operator=(DescriptorSetLayout&& other) noexcept { + swap(*this, other); + if(other) + other.destroy(); + return *this; +} + + +void DescriptorSetLayout::destroy() noexcept { + assert(!is_null()); + assert(m_context); + + vkDestroyDescriptorSetLayout( + m_context->device(), + m_descriptor_set_layout, + nullptr + ); + + m_descriptor_set_layout = nullptr; +} + + +} diff --git a/src/vk/DescriptorSetLayout.h b/src/vk/DescriptorSetLayout.h new file mode 100644 index 0000000..4c3181c --- /dev/null +++ b/src/vk/DescriptorSetLayout.h @@ -0,0 +1,63 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + +#include + + +namespace vk { + + +class DescriptorSetLayout { +public: + DescriptorSetLayout() noexcept; + DescriptorSetLayout(Context& context, Array bindings); + DescriptorSetLayout(const DescriptorSetLayout&) = delete; + DescriptorSetLayout(DescriptorSetLayout&& other) noexcept; + ~DescriptorSetLayout() noexcept; + + DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; + DescriptorSetLayout& operator=(DescriptorSetLayout&& other) noexcept; + + explicit inline operator bool() const noexcept { + return !is_null(); + } + + inline bool is_null() const noexcept { + return m_descriptor_set_layout == VK_NULL_HANDLE; + } + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline operator VkDescriptorSetLayout() noexcept { + return m_descriptor_set_layout; + } + + inline VkDescriptorSetLayout descriptor_set_layout() noexcept { + return m_descriptor_set_layout; + } + + friend inline void swap(DescriptorSetLayout& descriptor_set_layout_0, DescriptorSetLayout& descriptor_set_layout_1) noexcept { + using std::swap; + swap(descriptor_set_layout_0.m_context, descriptor_set_layout_1.m_context); + swap(descriptor_set_layout_0.m_descriptor_set_layout, descriptor_set_layout_1.m_descriptor_set_layout); + } + + void destroy() noexcept; + +private: + Context* m_context = nullptr; + VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; +}; + + +} diff --git a/src/vk/PipelineLayout.cpp b/src/vk/PipelineLayout.cpp new file mode 100644 index 0000000..bf9ab46 --- /dev/null +++ b/src/vk/PipelineLayout.cpp @@ -0,0 +1,73 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +PipelineLayout::PipelineLayout() noexcept { +} + +PipelineLayout::PipelineLayout( + Context& context, + Array set_layouts, + Array push_constant_ranges +) + : m_context(&context) +{ + assert(m_context); + + VkPipelineLayoutCreateInfo create_info { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = uint32_t(set_layouts.size()), + .pSetLayouts = set_layouts.data(), + .pushConstantRangeCount = uint32_t(push_constant_ranges.size()), + .pPushConstantRanges = push_constant_ranges.data(), + }; + if(vkCreatePipelineLayout( + context.device(), + &create_info, + nullptr, + &m_pipeline_layout + ) != VK_SUCCESS) + throw std::runtime_error("failed to create pipeline layout"); +} + +PipelineLayout::PipelineLayout(PipelineLayout&& other) noexcept +{ + swap(*this, other); +} + +PipelineLayout::~PipelineLayout() noexcept { + if(!is_null()) + destroy(); +} + + +PipelineLayout& PipelineLayout::operator=(PipelineLayout&& other) noexcept { + swap(*this, other); + if(other) + other.destroy(); + return *this; +} + + +void PipelineLayout::destroy() noexcept { + assert(!is_null()); + assert(m_context); + + vkDestroyPipelineLayout( + m_context->device(), + m_pipeline_layout, + nullptr + ); + + m_pipeline_layout = nullptr; +} + + +} diff --git a/src/vk/PipelineLayout.h b/src/vk/PipelineLayout.h new file mode 100644 index 0000000..afd6c60 --- /dev/null +++ b/src/vk/PipelineLayout.h @@ -0,0 +1,67 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + +#include + + +namespace vk { + + +class PipelineLayout { +public: + PipelineLayout() noexcept; + PipelineLayout( + Context& context, + Array set_layouts, + Array push_constant_ranges={} + ); + PipelineLayout(const PipelineLayout&) = delete; + PipelineLayout(PipelineLayout&& other) noexcept; + ~PipelineLayout() noexcept; + + PipelineLayout& operator=(const PipelineLayout&) = delete; + PipelineLayout& operator=(PipelineLayout&& other) noexcept; + + explicit inline operator bool() const noexcept { + return !is_null(); + } + + inline bool is_null() const noexcept { + return m_pipeline_layout == VK_NULL_HANDLE; + } + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline operator VkPipelineLayout() noexcept { + return m_pipeline_layout; + } + + inline VkPipelineLayout pipeline_layout() noexcept { + return m_pipeline_layout; + } + + friend inline void swap(PipelineLayout& pipeline_layout_0, PipelineLayout& pipeline_layout_1) noexcept { + using std::swap; + swap(pipeline_layout_0.m_context, pipeline_layout_1.m_context); + swap(pipeline_layout_0.m_pipeline_layout, pipeline_layout_1.m_pipeline_layout); + } + + void destroy() noexcept; + +private: + Context* m_context = nullptr; + VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; +}; + + +} diff --git a/src/vk/Wrapper.cpp b/src/vk/Wrapper.cpp new file mode 100644 index 0000000..6c3d5e5 --- /dev/null +++ b/src/vk/Wrapper.cpp @@ -0,0 +1,48 @@ +// Copyright 2022 Simon Boyé + +#include +#include + +#include + + +namespace vk { + + +Wrapper::Wrapper() noexcept { +} + +Wrapper::Wrapper(Context& context, Flags flags) noexcept + : m_context(&context) + , m_flags(flags) +{ + assert(m_context); +} + +Wrapper::Wrapper(const Wrapper& other) noexcept + : m_context(other.m_context) +{} + +Wrapper::Wrapper(Wrapper&& other) noexcept +{ + swap(other); +} + +Wrapper::~Wrapper() noexcept { +} + + +Wrapper& Wrapper::operator=(const Wrapper& other) noexcept { + if (&other != this) { + m_context = other.m_context; + } + return *this; +} + +Wrapper& Wrapper::operator=(Wrapper&& other) noexcept { + swap(other); + return *this; +} + + +} diff --git a/src/vk/Wrapper.h b/src/vk/Wrapper.h new file mode 100644 index 0000000..a2d9913 --- /dev/null +++ b/src/vk/Wrapper.h @@ -0,0 +1,56 @@ +// Copyright 2022 Simon Boyé +#pragma once + +#include + +#include + + +namespace vk { + + +class Wrapper { +public: + enum Flag { + OwnUnderlyingObject = 0x01, + }; + using Flags = unsigned; + +public: + Wrapper() noexcept; + Wrapper(Context& context, Flags flags=OwnUnderlyingObject) noexcept; + Wrapper(const Wrapper& other) noexcept; + Wrapper(Wrapper&& other) noexcept; + ~Wrapper() noexcept; + + Wrapper& operator=(const Wrapper& other) noexcept; + Wrapper& operator=(Wrapper&& other) noexcept; + + inline const Context* context() const noexcept { + return m_context; + } + + inline Context* context() noexcept { + return m_context; + } + + inline bool own_underlying_object() const noexcept { + return (m_flags & Flags(OwnUnderlyingObject)) != 0; + } + + inline void swap(Wrapper& other) noexcept { + using std::swap; + swap(m_context, other.m_context); + } + + friend inline void swap(Wrapper& wrapper_0, Wrapper& wrapper_1) noexcept { + wrapper_0.swap(wrapper_1); + } + +protected: + Context* m_context = nullptr; + Flags m_flags = 0; +}; + + +}