diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 06277b312..30bbdc89f 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -39,6 +39,7 @@ #include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPoolCreateInfo.h" #include "Magnum/Vk/ComputePipelineCreateInfo.h" +#include "Magnum/Vk/DescriptorSetLayoutCreateInfo.h" #include "Magnum/Vk/DeviceCreateInfo.h" #include "Magnum/Vk/DeviceFeatures.h" #include "Magnum/Vk/DeviceProperties.h" @@ -312,6 +313,55 @@ fence.wait(); /* [CommandBuffer-usage-submit] */ } +{ +Vk::Device device{NoCreate}; +/* The include should be a no-op here since it was already included above */ +/* [DescriptorSetLayout-creation] */ +#include + +DOXYGEN_IGNORE() + +Vk::DescriptorSetLayout layout{device, Vk::DescriptorSetLayoutCreateInfo{ + {{0, Vk::DescriptorType::UniformBuffer}}, + {{1, Vk::DescriptorType::CombinedImageSampler, 1, + Vk::ShaderStage::Fragment}} +}}; +/* [DescriptorSetLayout-creation] */ +} + +{ +Vk::Device device{NoCreate}; +/* [DescriptorSetLayout-creation-immutable-samplers] */ +Vk::Sampler sampler{DOXYGEN_IGNORE(NoCreate)}; + +Vk::DescriptorSetLayout layout{device, Vk::DescriptorSetLayoutCreateInfo{ + {{0, Vk::DescriptorType::UniformBuffer}}, + {{1, Vk::DescriptorType::CombinedImageSampler, {sampler}, + Vk::ShaderStage::Fragment}} +}}; +/* [DescriptorSetLayout-creation-immutable-samplers] */ +} + +{ +Vk::Instance instance{NoCreate}; +/* [DescriptorSetLayout-creation-binding-flags] */ +Vk::Device device{instance, Vk::DeviceCreateInfo{DOXYGEN_IGNORE(Vk::pickDevice(instance))} + DOXYGEN_IGNORE() + .setEnabledFeatures( + Vk::DeviceFeature::DescriptorBindingUniformBufferUpdateAfterBind| + DOXYGEN_IGNORE(Vk::DeviceFeatures{}) + ) +}; + +Vk::DescriptorSetLayout layout{device, Vk::DescriptorSetLayoutCreateInfo{ + {{0, Vk::DescriptorType::UniformBuffer, 1, + ~Vk::ShaderStages{}, + Vk::DescriptorSetLayoutBinding::Flag::UpdateAfterBind}}, + DOXYGEN_IGNORE() +}}; +/* [DescriptorSetLayout-creation-binding-flags] */ +} + { Vk::Instance instance; /* The include should be a no-op here since it was already included above */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 8f4078bc2..55dde6ffb 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -60,7 +60,7 @@ Vulkan handle | Matching API @type_vk{DebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT** | | @type_vk{DescriptorPool} | | @type_vk{DescriptorSet} | | -@type_vk{DescriptorSetLayout} | | +@type_vk{DescriptorSetLayout} | @ref DescriptorSetLayout @type_vk{DescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{Device} | @ref Device @type_vk{DeviceMemory} | @ref Memory @@ -191,7 +191,7 @@ Vulkan function | Matching API @fn_vk{CreateDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CreateDeferredOperationKHR} @m_class{m-label m-flat m-warning} **KHR**, \n @fn_vk{DestroyDeferredOperationKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CreateDescriptorPool}, \n @fn_vk{DestroyDescriptorPool} | | -@fn_vk{CreateDescriptorSetLayout}, \n @fn_vk{DestroyDescriptorSetLayout} | | +@fn_vk{CreateDescriptorSetLayout}, \n @fn_vk{DestroyDescriptorSetLayout} | @ref DescriptorSetLayout constructor and destructor @fn_vk{CreateDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1**, \n @fn_vk{DestroyDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | @ref Device constructor and destructor @fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | @@ -466,10 +466,9 @@ Vulkan structure | Matching API @type_vk{DescriptorPoolCreateInfo} | | @type_vk{DescriptorPoolSize} | | @type_vk{DescriptorSetAllocateInfo} | | -@type_vk{DescriptorSetLayoutBinding} | | -@type_vk{DescriptorSetLayoutBindingFlagsCreateInfo} | | -@type_vk{DescriptorSetLayoutCreateInfo} | | -@type_vk{DescriptorSetLayoutBindingFlagsCreateInfo} @m_class{m-label m-flat m-success} **EXT, 1.2** | | +@type_vk{DescriptorSetLayoutBinding} | @ref DescriptorSetLayoutBinding +@type_vk{DescriptorSetLayoutBindingFlagsCreateInfo} @m_class{m-label m-flqat m-success} **EXT, 1.2** | @ref DescriptorSetLayoutCreateInfo +@type_vk{DescriptorSetLayoutCreateInfo} | @ref DescriptorSetLayoutCreateInfo @type_vk{DescriptorSetLayoutSupport} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{DescriptorSetVariableDescriptorCountAllocateInfo} @m_class{m-label m-flat m-success} **EXT, 1.2** | | @type_vk{DescriptorSetVariableDescriptorCountLayoutSupport} @m_class{m-label m-flat m-success} **EXT, 1.2** | | @@ -826,11 +825,11 @@ Vulkan enum | Matching API @type_vk{DebugReportObjectTypeEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | @type_vk{DebugUtilsMessageSeverityFlagBitsEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @type_vk{DebugUtilsMessageTypeFlagsEXT} @m_class{m-label m-flat m-warning} **EXT** | | @type_vk{DependencyFlagBits}, \n @type_vk{DependencyFlags} | @ref DependencyFlag, \n @ref DependencyFlags -@type_vk{DescriptorBindingFlagBits} @m_class{m-label m-flat m-success} **EXT, 1.2**, \n @type_vk{DescriptorBindingFlags} @m_class{m-label m-flat m-success} **EXT, 1.2** | | +@type_vk{DescriptorBindingFlagBits} @m_class{m-label m-flat m-success} **EXT, 1.2**, \n @type_vk{DescriptorBindingFlags} @m_class{m-label m-flat m-success} **EXT, 1.2** | @ref DescriptorSetLayoutBinding::Flag, \n @ref DescriptorSetLayoutBinding::Flags @type_vk{DescriptorPoolCreateFlagBits}, \n @type_vk{DescriptorPoolCreateFlags} | | -@type_vk{DescriptorSetLayoutCreateFlagBits}, \n @type_vk{DescriptorSetLayoutCreateFlags} | | +@type_vk{DescriptorSetLayoutCreateFlagBits}, \n @type_vk{DescriptorSetLayoutCreateFlags} | @ref DescriptorSetLayoutCreateInfo::Flag, \n @ref DescriptorSetLayoutCreateInfo::Flags @type_vk{DescriptorUpdateTemplateType} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@type_vk{DescriptorType} | | +@type_vk{DescriptorType} | @ref DescriptorType @type_vk{DeviceQueueCreateFlagBits}, \n @type_vk{DeviceQueueCreateFlags} | | @type_vk{DriverId} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref DeviceDriver @type_vk{DynamicState} | @ref DynamicRasterizationState diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 9791802f3..ce90aeae7 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -90,7 +90,7 @@ Extension | Status @vk_extension{KHR,create_renderpass2} | done @vk_extension{EXT,sampler_filter_minmax} | | @vk_extension{KHR,image_format_list} | | -@vk_extension{EXT,descriptor_indexing} | | +@vk_extension{EXT,descriptor_indexing} | done except properties and variable descriptor count allocation @vk_extension{EXT,shader_viewport_index_layer} | | @vk_extension{KHR,draw_indirect_count} | | @vk_extension{KHR,shader_subgroup_extended_types} | | diff --git a/src/Magnum/Vk/BufferCreateInfo.h b/src/Magnum/Vk/BufferCreateInfo.h index 59e3a277d..8fb6b0055 100644 --- a/src/Magnum/Vk/BufferCreateInfo.h +++ b/src/Magnum/Vk/BufferCreateInfo.h @@ -64,16 +64,28 @@ enum class BufferUsage: UnsignedInt { */ TransferDestination = VK_BUFFER_USAGE_TRANSFER_DST_BIT, - /** Suitable for creating a uniform texel buffer view */ + /** + * Suitable for creating a uniform texel buffer view. + * @see @ref DescriptorType::UniformTexelBuffer + */ UniformTexelBuffer = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, - /** Suitable for creating a storage texel buffer view */ + /** + * Suitable for creating a storage texel buffer view. + * @see @ref DescriptorType::StorageTexelBuffer + */ StorageTexelBuffer = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, - /** Suitable for a uniform buffer */ + /** + * Suitable for a uniform buffer. + * @see @ref DescriptorType::UniformBuffer + */ UniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - /** Suitable for a storage buffer */ + /** + * Suitable for a storage buffer. + * @see @ref DescriptorType::StorageBuffer + */ StorageBuffer = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, /** diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 7a2a08b57..fcebd1574 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -29,6 +29,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS CommandBuffer.cpp CommandPool.cpp + DescriptorSetLayout.cpp Extensions.cpp Fence.cpp Framebuffer.cpp @@ -72,6 +73,8 @@ set(MagnumVk_HEADERS CommandPool.h CommandPoolCreateInfo.h ComputePipelineCreateInfo.h + DescriptorSetLayout.h + DescriptorSetLayoutCreateInfo.h Device.h DeviceCreateInfo.h DeviceFeatures.h diff --git a/src/Magnum/Vk/DescriptorSetLayout.cpp b/src/Magnum/Vk/DescriptorSetLayout.cpp new file mode 100644 index 000000000..6e0858432 --- /dev/null +++ b/src/Magnum/Vk/DescriptorSetLayout.cpp @@ -0,0 +1,216 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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 "DescriptorSetLayout.h" +#include "DescriptorSetLayoutCreateInfo.h" + +#include +#include +#include + +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(const UnsignedInt binding, const DescriptorType descriptorType, const UnsignedInt descriptorCount, const ShaderStages stages, Flags flags): _binding{}, _flags{VkDescriptorBindingFlags(flags)} { + _binding.binding = binding; + _binding.descriptorType = VkDescriptorType(descriptorType); + _binding.descriptorCount = descriptorCount; + _binding.stageFlags = VkShaderStageFlags(stages); +} + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(const UnsignedInt binding, const DescriptorType descriptorType, const Containers::ArrayView immutableSamplers, const ShaderStages stages, Flags flags): _binding{}, _flags{VkDescriptorBindingFlags(flags)} { + Containers::ArrayView immutableSamplersCopy; + _data = Containers::ArrayTuple{ + {Containers::NoInit, immutableSamplers.size(), immutableSamplersCopy} + }; + Utility::copy(immutableSamplers, immutableSamplersCopy); + + _binding.binding = binding; + _binding.descriptorType = VkDescriptorType(descriptorType); + _binding.descriptorCount = immutableSamplers.size(); + _binding.pImmutableSamplers = immutableSamplersCopy; + _binding.stageFlags = VkShaderStageFlags(stages); +} + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(const UnsignedInt binding, const DescriptorType descriptorType, const std::initializer_list immutableSamplers, const ShaderStages stages, Flags flags): DescriptorSetLayoutBinding{binding, descriptorType, Containers::arrayView(immutableSamplers), stages, flags} {} + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(NoInitT) noexcept {} + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(const VkDescriptorSetLayoutBinding& binding, const VkDescriptorBindingFlags flags): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _binding(binding), + _flags{flags} {} + +DescriptorSetLayoutBinding::DescriptorSetLayoutBinding(DescriptorSetLayoutBinding&& other) noexcept: + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _binding(other._binding), + _data{std::move(other._data)}, + _flags{other._flags} +{ + /* 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._binding.pImmutableSamplers = nullptr; +} + +DescriptorSetLayoutBinding::~DescriptorSetLayoutBinding() = default; + +DescriptorSetLayoutBinding& DescriptorSetLayoutBinding::operator=(DescriptorSetLayoutBinding&& other) noexcept { + using std::swap; + swap(other._binding, _binding); + swap(other._data, _data); + swap(other._flags, _flags); + return *this; +} + +DescriptorSetLayoutCreateInfo::DescriptorSetLayoutCreateInfo(const Containers::ArrayView> bindings, const Flags flags): _info{} { + /* Check the total count of immutable samplers to allocate them all in a + contiguous memory location. Also check if we have any binding flags. If + yes, we have to create an additional array and put a structure into the + pNext chain. */ + std::size_t immutableSamplerCount = 0; + bool hasBindingFlags = false; + for(const DescriptorSetLayoutBinding& b: bindings) { + if(b->pImmutableSamplers) immutableSamplerCount += b->descriptorCount; + if(b.flags()) hasBindingFlags = true; + } + + /* Allocate a single block of memory for everything. (I'm still amazed at + how simple this is and yet how useful.) */ + Containers::ArrayView bindingsCopy; + Containers::ArrayView bindingFlagsCopy; + Containers::ArrayView bindingsCreateInfoView; + Containers::ArrayView immutableSamplersCopy; + _data = Containers::ArrayTuple{ + {Containers::NoInit, bindings.size(), bindingsCopy}, + {Containers::NoInit, hasBindingFlags ? bindings.size() : 0, bindingFlagsCopy}, + {Containers::ValueInit, hasBindingFlags ? 1u : 0u, bindingsCreateInfoView}, + {Containers::NoInit, immutableSamplerCount, immutableSamplersCopy} + }; + + /* Copy the binding and then for each manually copy and reroute the + immutable samplers, if any; copy the flags as well if there are any */ + std::size_t immutableSamplerOffset = 0; + for(std::size_t i = 0; i != bindings.size(); ++i) { + const DescriptorSetLayoutBinding& b = bindings[i]; + bindingsCopy[i] = b; + if(b->pImmutableSamplers) { + Utility::copy( + {b->pImmutableSamplers, b->descriptorCount}, + immutableSamplersCopy.slice(immutableSamplerOffset, immutableSamplerOffset + b->descriptorCount)); + bindingsCopy[i].pImmutableSamplers = immutableSamplersCopy + immutableSamplerOffset; + immutableSamplerOffset += b->descriptorCount; + } + if(hasBindingFlags) bindingFlagsCopy[i] = b.flags(); + } + CORRADE_INTERNAL_ASSERT(immutableSamplerOffset == immutableSamplerCount); + + _info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + _info.flags = VkDescriptorSetLayoutCreateFlags(flags); + _info.bindingCount = bindings.size(); + _info.pBindings = bindingsCopy; + if(hasBindingFlags) { + bindingsCreateInfoView[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO; + bindingsCreateInfoView[0].bindingCount = bindings.size(); + bindingsCreateInfoView[0].pBindingFlags = bindingFlagsCopy; + _info.pNext = bindingsCreateInfoView; + } +} + +DescriptorSetLayoutCreateInfo::DescriptorSetLayoutCreateInfo(const std::initializer_list> bindings, const Flags flags): DescriptorSetLayoutCreateInfo{Containers::arrayView(bindings), flags} {} + +DescriptorSetLayoutCreateInfo::DescriptorSetLayoutCreateInfo(NoInitT) noexcept {} + +DescriptorSetLayoutCreateInfo::DescriptorSetLayoutCreateInfo(const VkDescriptorSetLayoutCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +DescriptorSetLayoutCreateInfo::DescriptorSetLayoutCreateInfo(DescriptorSetLayoutCreateInfo&& 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), + _data{std::move(other._data)} +{ + /* 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.bindingCount = 0; + other._info.pBindings = nullptr; +} + +DescriptorSetLayoutCreateInfo::~DescriptorSetLayoutCreateInfo() = default; + +DescriptorSetLayoutCreateInfo& DescriptorSetLayoutCreateInfo::operator=(DescriptorSetLayoutCreateInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._data, _data); + return *this; +} + +DescriptorSetLayout DescriptorSetLayout::wrap(Device& device, const VkDescriptorSetLayout handle, const HandleFlags flags) { + DescriptorSetLayout out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +DescriptorSetLayout::DescriptorSetLayout(Device& device, const DescriptorSetLayoutCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateDescriptorSetLayout(device, info, nullptr, &_handle)); +} + +DescriptorSetLayout::DescriptorSetLayout(NoCreateT): _device{}, _handle{} {} + +DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +DescriptorSetLayout::~DescriptorSetLayout() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyDescriptorSetLayout(*_device, _handle, nullptr); +} + +DescriptorSetLayout& DescriptorSetLayout::operator=(DescriptorSetLayout&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkDescriptorSetLayout DescriptorSetLayout::release() { + const VkDescriptorSetLayout handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/DescriptorSetLayout.h b/src/Magnum/Vk/DescriptorSetLayout.h new file mode 100644 index 000000000..1e4c4167b --- /dev/null +++ b/src/Magnum/Vk/DescriptorSetLayout.h @@ -0,0 +1,176 @@ +#ifndef Magnum_Vk_DescriptorSetLayout_h +#define Magnum_Vk_DescriptorSetLayout_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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::DescriptorSetLayout + * @m_since_latest + */ + +#include "Magnum/Tags.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/visibility.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { + +/** +@brief Descriptor set layout +@m_since_latest + +Wraps a @type_vk_keyword{DescriptorSetLayout}. A descriptor set layout lists +descriptors (such as uniform buffers or samplers) used by shaders in a +@ref Pipeline. + +@section Vk-DescriptorSetLayout-creation Descriptor set layout creation + +The @ref DescriptorSetLayoutCreateInfo class takes one or more +@ref DescriptorSetLayoutBinding entries, where each specifies the binding +number, descriptor type, descriptor count in case of descriptor arrays and +which shader stages are designed to access the binding. In the following +example one uniform buffer binding @cpp 0 @ce is accessible by any stages and +one combined image/sampler binding @cpp 1 @ce is accessed only by +@link ShaderStage::Fragment @endlink: + +@snippet MagnumVk.cpp DescriptorSetLayout-creation + + + +@m_class{m-note m-info} + +@par + Note that in C++11 you have to use double brackets (@cpp {{ }} @ce) to + initialize the @ref DescriptorSetLayoutBinding entries, as shown above. + With C++14 and newer it should work with just a single pair of brackets. + +@subsection Vk-DescriptorSetLayout-creation-immutable-samplers Immutable samplers + +For @ref DescriptorType::Sampler and @ref DescriptorType::CombinedImageSampler +it's possible to specify a list of immutable @ref Sampler "Samplers" in place +of the descriptor count argument. The descriptor count is then implicitly taken +from size of the array. The above snippet with immutable samplers would look +like this: + +@snippet MagnumVk.cpp DescriptorSetLayout-creation-immutable-samplers + +@subsection Vk-DescriptorSetLayout-creation-binding-flags Descriptor binding flags + +With Vulkan 1.2 or @vk_extension{EXT,descriptor_indexing} it's possible to +specify additional flags per binding. All of them require a certain +@ref DeviceFeature to be supported and enabled, see particular +@ref DescriptorSetLayoutBinding::Flag for more information: + +@snippet MagnumVk.cpp DescriptorSetLayout-creation-binding-flags +*/ +class MAGNUM_VK_EXPORT DescriptorSetLayout { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the descriptor set layout is + * created on + * @param handle The @type_vk{DescriptorSetLayout} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a descriptor set layout created using a constructor, the Vulkan + * descriptor set layout is by default not deleted on destruction, use + * @p flags for different behavior. + * @see @ref release() + */ + static DescriptorSetLayout wrap(Device& device, VkDescriptorSetLayout handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to create the descriptor set layout + * on + * @param info Descriptor set layout creation info + * + * @see @fn_vk_keyword{CreateDescriptorSetLayout} + */ + explicit DescriptorSetLayout(Device& device, const DescriptorSetLayoutCreateInfo& info); + + /** + * @brief Construct without creating the descriptor set layout + * + * 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 DescriptorSetLayout(NoCreateT); + + /** @brief Copying is not allowed */ + DescriptorSetLayout(const DescriptorSetLayout&) = delete; + + /** @brief Move constructor */ + DescriptorSetLayout(DescriptorSetLayout&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{DescriptorSetLayout} handle, unless the + * instance was created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyDescriptorSetLayout}, @ref release() + */ + ~DescriptorSetLayout(); + + /** @brief Copying is not allowed */ + DescriptorSetLayout& operator=(const DescriptorSetLayout&) = delete; + + /** @brief Move assignment */ + DescriptorSetLayout& operator=(DescriptorSetLayout&& other) noexcept; + + /** @brief Underlying @type_vk{DescriptorSetLayout} handle */ + VkDescriptorSetLayout handle() { return _handle; } + /** @overload */ + operator VkDescriptorSetLayout() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan descriptor set layout + * + * Releases ownership of the Vulkan descriptor set layout and returns + * its handle so @fn_vk{DestroyDescriptorSetLayout} is not called on + * destruction. The internal state is then equivalent to moved-from + * state. + * @see @ref wrap() + */ + VkDescriptorSetLayout release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkDescriptorSetLayout _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/DescriptorSetLayoutCreateInfo.h b/src/Magnum/Vk/DescriptorSetLayoutCreateInfo.h new file mode 100644 index 000000000..dcb62bfb2 --- /dev/null +++ b/src/Magnum/Vk/DescriptorSetLayoutCreateInfo.h @@ -0,0 +1,480 @@ +#ifndef Magnum_Vk_DescriptorSetLayoutCreateInfo_h +#define Magnum_Vk_DescriptorSetLayoutCreateInfo_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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::DescriptorSetLayoutCreateInfo, @ref Magnum::Vk::DescriptorSetLayoutBinding, enum @ref Magnum::Vk::DescriptorType + * @m_since_latest + */ + +/* While I'm not commonly including the Reference headers, the constructor of + DescriptorSetLayoutCreateInfo needs this type and so users would be forced + to include it every time they include this header. Or forget to do so and + wonder why the code is not compiling, which especially when combined with + the fact that one has to wrap the DescriptorSetLayoutBinding instances in + double {{}}'s might be a bit too much to handle. */ +#include + +#include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Vk/visibility.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { + +/** +@brief Descriptor type +@m_since_latest + +Wraps @type_vk_keyword{DescriptorType}. +@see @ref DescriptorSetLayoutBinding +@m_enum_values_as_keywords +*/ +enum class DescriptorType: Int { + /** + * @ref Sampler. + * + * @see @ref DescriptorType::CombinedImageSampler + */ + Sampler = VK_DESCRIPTOR_TYPE_SAMPLER, + + /** + * @ref Sampler combined with an @ref Image. + * + * @m_class{m-note m-success} + * + * @par + * On some implementations it may be more efficient to sample from an + * a combined image sampler than a separate + * @ref DescriptorType::Sampler and @ref DescriptorType::SampledImage. + * + * The image is expected to have been created with @ref ImageUsage::Sampled + * and be in either @ref ImageLayout::General or + * @ref ImageLayout::ShaderReadOnly. + */ + CombinedImageSampler = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + + /** + * Sampled @ref Image. + * + * The image is expected to have been created with @ref ImageUsage::Sampled + * and be in either @ref ImageLayout::General or + * @ref ImageLayout::ShaderReadOnly. + * @see @ref DescriptorType::CombinedImageSampler + */ + SampledImage = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + + /** + * Storage @ref Image. + * + * The image is expected to have been created with @ref ImageUsage::Storage + * and be in @ref ImageLayout::General. + */ + StorageImage = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + + /** + * Uniform texel buffer view. + * + * The buffer is expected to have been created with + * @ref BufferUsage::UniformTexelBuffer. + */ + UniformTexelBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + + /** + * Storage texel buffer view. + * + * The buffer is expected to have been created with + * @ref BufferUsage::StorageTexelBuffer. + */ + StorageTexelBuffer = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + + /** + * Uniform @ref Buffer. + * + * The buffer is expected to have been created with + * @ref BufferUsage::UniformBuffer. + */ + UniformBuffer = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + + /** + * Storage @ref Buffer. + * + * The buffer is expected to have been created with + * @ref BufferUsage::StorageBuffer. + */ + StorageBuffer = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + + /** + * Uniform @ref Buffer with a dynamic offset. + * + * The buffer is expected to have been created with + * @ref BufferUsage::UniformBuffer. + */ + UniformBufferDynamic = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + + /** + * Storage @ref Buffer with a dynamic offset. + * + * The buffer is expected to have been created with + * @ref BufferUsage::StorageBuffer. + */ + StorageBufferDynamic = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, + + /** + * Input @ref Image attachment. + * + * The image is expected to have been created with + * @ref ImageUsage::InputAttachment and be in @ref ImageLayout::General. + */ + InputAttachment = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + + /** + * Acceleration structure. + * + * @requires_vk_feature @ref DeviceFeature::AccelerationStructure + */ + AccelerationStructure = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR +}; + +/** +@brief Descriptor set layout binding +@m_since_latest + +Wraps a @type_vk_keyword{DescriptorSetLayoutBinding} together with +associated @type_vk_keyword{DescriptorBindingFlags}. See +@ref Vk-DescriptorSetLayout-creation "Descriptor set layout creation" +for usage information. +*/ +class MAGNUM_VK_EXPORT DescriptorSetLayoutBinding { + public: + /** + * @brief Descriptor set layout binding flag + * + * Wraps @type_vk_keyword{DescriptorBindingFlagBits}. + * @see @ref Flags, @ref DescriptorSetLayoutBinding() + * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** + * If descriptors in this binding are updated between + * binding them in a command buffer and a @ref Queue::submit(), the + * submission will use the most recently set descriptors for + * the binding and the updates do not invalidate the command + * buffer. + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingSampledImageUpdateAfterBind + * if used on a @ref DescriptorType::CombinedImageSampler / + * @relativeref{DescriptorType,SampledImage} + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingStorageImageUpdateAfterBind + * if used on a @ref DescriptorType::StorageImage + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingUniformTexelBufferUpdateAfterBind + * if used on a @ref DescriptorType::UniformTexelBuffer + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingStorageTexelBufferUpdateAfterBind + * if used on a @ref DescriptorType::StorageTexelBuffer + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingUniformBufferUpdateAfterBind + * if used on a @ref DescriptorType::UniformBuffer / + * @relativeref{DescriptorType,UniformBufferDynamic} + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingStorageBufferUpdateAfterBind + * if used on a @ref DescriptorType::StorageBuffer / + * @relativeref{DescriptorType,StorageBufferDynamic} + */ + UpdateAfterBind = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + + /** + * Descriptors not used by the command buffer can be updated + * after binding them in a command buffer and while the command + * buffer is pending execution. If set together with + * @ref Flag::PartiallyBound, any descriptors that are not + * dynamically used can be updated, if alone then only descriptors + * statically not used can be updated. + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingUpdateUnusedWhilePending + */ + UpdateUnusedWhilePending = VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT, + + /** + * Descriptors in the binding that are not dynamically used don't + * need to contain valid descriptors when consumed. + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingPartiallyBound + */ + PartiallyBound = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT, + + /** + * This descriptor binding has a variable size that will be + * specified when a descriptor set is allocated using this layout, + * and the @p descriptorCount value is treated as an upper bound. + * @requires_vk_feature @ref DeviceFeature::DescriptorBindingVariableDescriptorCount + */ + VariableDescriptorCount = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT + }; + + /** + * @brief Descriptor set layout creation flags + * + * Type-safe wrapper for @type_vk_keyword{DescriptorBindingFlags}. + * @see @ref DescriptorSetLayoutBinding() + * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param binding Binding corresponding to a particular + * binding in a shader + * @param descriptorType Descriptor type + * @param descriptorCount Number of descriptors contained in the + * binding. Has to be at least @cpp 1 @ce. If the shader binding + * is not an array, use @cpp 1 @ce. + * @param stages Shader stages that access the binding. + * Use @cpp ~Vk::ShaderStages{} @ce to specify that all stages + * may access the binding. + * @param flags Flags + * + * The following @type_vk{DescriptorSetLayoutBinding} fields are + * pre-filled, everything else is zero-filled: + * + * - `binding` + * - `descriptorType` + * - `descriptorCount` + * - `stageFlags` to @p stages + * + * If @p flags are non-empty, a @type_vk{DescriptorBindingFlags} field + * is saved and then subsequently available through @ref flags(). + * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} if + * @p flags are non-empty + */ + /*implicit*/ DescriptorSetLayoutBinding(UnsignedInt binding, DescriptorType descriptorType, UnsignedInt descriptorCount = 1, ShaderStages stages = ~ShaderStages{}, Flags flags = {}); + + /** + * @brief Construct with immutable samplers + * @param binding Binding corresponding to a particular + * binding in a shader + * @param descriptorType Descriptor type. Should be either + * @ref DescriptorType::Sampler or + * @ref DescriptorType::CombinedImageSampler. + * @param immutableSamplers Immutable samplers + * @param stages Shader stages that access the binding. + * Use @cpp ~Vk::ShaderStages{} @ce to specify that all stages + * may access the binding. + * @param flags Flags + * + * The following @type_vk{DescriptorSetLayoutBinding} fields are + * pre-filled, everything else is zero-filled: + * + * - `binding` + * - `descriptorType` + * - `descriptorCount` to @cpp immutableSamplers.size() @ce + * - `pImmutableSamplers` to a copy of @p immutableSamplers + * - `stageFlags` to @p stages + * + * If @p flags are non-empty, a @type_vk{DescriptorBindingFlags} field + * is saved and then subsequently available through @ref flags(). + * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} if + * @p flags are non-empty + */ + /*implicit*/ DescriptorSetLayoutBinding(UnsignedInt binding, DescriptorType descriptorType, Containers::ArrayView immutableSamplers, ShaderStages stages = ~ShaderStages{}, Flags flags = {}); + /** @overload */ + /*implicit*/ DescriptorSetLayoutBinding(UnsignedInt binding, DescriptorType descriptorType, std::initializer_list immutableSamplers, ShaderStages stages = ~ShaderStages{}, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that the structure has to be fully initialized afterwards in + * order to be usable. + */ + explicit DescriptorSetLayoutBinding(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 DescriptorSetLayoutBinding(const VkDescriptorSetLayoutBinding& info, VkDescriptorBindingFlags flags = {}); + + /** @brief Copying is not allowed */ + DescriptorSetLayoutBinding(const DescriptorSetLayoutBinding&) = delete; + + /** @brief Move constructor */ + DescriptorSetLayoutBinding(DescriptorSetLayoutBinding&& other) noexcept; + + ~DescriptorSetLayoutBinding(); + + /** @brief Copying is not allowed */ + DescriptorSetLayoutBinding& operator=(const DescriptorSetLayoutBinding&) = delete; + + /** @brief Move assignment */ + DescriptorSetLayoutBinding& operator=(DescriptorSetLayoutBinding&& other) noexcept; + + /** @brief Underlying @type_vk{DescriptorSetLayoutBinding} structure */ + VkDescriptorSetLayoutBinding& operator*() { return _binding; } + /** @overload */ + const VkDescriptorSetLayoutBinding& operator*() const { return _binding; } + /** @overload */ + VkDescriptorSetLayoutBinding* operator->() { return &_binding; } + /** @overload */ + const VkDescriptorSetLayoutBinding* operator->() const { return &_binding; } + /** @overload */ + operator const VkDescriptorSetLayoutBinding*() const { return &_binding; } + + /** + * @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 VkDescriptorSetLayoutBinding&() const { return _binding; } + + /** @brief Underlying @type_vk{DescriptorBindingFlags} enum set */ + VkDescriptorBindingFlags& flags() { return _flags; } + /** @overload */ + VkDescriptorBindingFlags flags() const { return _flags; } + + private: + VkDescriptorSetLayoutBinding _binding; + Containers::ArrayTuple _data; + VkDescriptorBindingFlags _flags; +}; + +CORRADE_ENUMSET_OPERATORS(DescriptorSetLayoutBinding::Flags) + +/** +@brief Descriptor set layout creation info +@m_since_latest + +Wraps a @type_vk_keyword{DescriptorSetLayoutCreateInfo} together with +@type_vk_keyword{DescriptorSetLayoutBindingFlagsCreateInfo}. See +@ref Vk-DescriptorSetLayout-creation "Descriptor set layout creation" +for usage information. +*/ +class MAGNUM_VK_EXPORT DescriptorSetLayoutCreateInfo { + public: + /** + * @brief Descriptor set layout creation flag + * + * Wraps @type_vk_keyword{DescriptorSetLayoutCreateFlagBits}. + * @see @ref Flags, @ref DescriptorSetLayoutCreateInfo() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** @todo all the flags from extensions and 1.2 */ + }; + + /** + * @brief Descriptor set layout creation flags + * + * Type-safe wrapper for @type_vk_keyword{DescriptorSetLayoutCreateFlags}. + * @see @ref DescriptorSetLayoutCreateInfo() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param bindings Descriptor set layout bindings. At least one + * binding has to be present. + * @param flags Descriptor set layout creation flags + * + * The following @type_vk{DescriptorSetLayoutCreateInfo} fields are + * pre-filled in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `bindingCount` to @cpp bindings.size() @ce + * - `pBindings` to a copy of @p bindings + * + * If any of the @p bindings has @ref DescriptorSetLayoutBinding::flags() + * non-empty, a @type_vk{DescriptorSetLayoutBindingFlagsCreateInfo} + * structure is referenced from the `pNext` chain of + * @type_vk{DescriptorSetLayoutCreateInfo}, with the following fields + * set in addition to `sType`, everything else zero-filled: + * + * - `bindingCount` to @cpp binding.size() @ce + * - `pBindingFlags` to a list of all + * @ref DescriptorSetLayoutBinding::flags() from @p bindings + */ + explicit DescriptorSetLayoutCreateInfo(Containers::ArrayView> bindings, Flags flags = {}); + /** @overload */ + explicit DescriptorSetLayoutCreateInfo(std::initializer_list> bindings, 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 DescriptorSetLayoutCreateInfo(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 DescriptorSetLayoutCreateInfo(const VkDescriptorSetLayoutCreateInfo& info); + + /** @brief Copying is not allowed */ + DescriptorSetLayoutCreateInfo(const DescriptorSetLayoutCreateInfo&) = delete; + + /** @brief Move constructor */ + DescriptorSetLayoutCreateInfo(DescriptorSetLayoutCreateInfo&& other) noexcept; + + ~DescriptorSetLayoutCreateInfo(); + + /** @brief Copying is not allowed */ + DescriptorSetLayoutCreateInfo& operator=(const DescriptorSetLayoutCreateInfo&) = delete; + + /** @brief Move assignment */ + DescriptorSetLayoutCreateInfo& operator=(DescriptorSetLayoutCreateInfo&& other) noexcept; + + /** @brief Underlying @type_vk{DescriptorSetLayoutCreateInfo} structure */ + VkDescriptorSetLayoutCreateInfo& operator*() { return _info; } + /** @overload */ + const VkDescriptorSetLayoutCreateInfo& operator*() const { return _info; } + /** @overload */ + VkDescriptorSetLayoutCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkDescriptorSetLayoutCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkDescriptorSetLayoutCreateInfo*() const { return &_info; } + + private: + VkDescriptorSetLayoutCreateInfo _info; + Containers::ArrayTuple _data; +}; + +CORRADE_ENUMSET_OPERATORS(DescriptorSetLayoutCreateInfo::Flags) + +}} + +/* Make the definition complete -- it doesn't make sense to have a CreateInfo + without the corresponding object anyway. */ +#include "Magnum/Vk/DescriptorSetLayout.h" + +#endif diff --git a/src/Magnum/Vk/DeviceFeatures.h b/src/Magnum/Vk/DeviceFeatures.h index 1d5d4c34c..00a68f61e 100644 --- a/src/Magnum/Vk/DeviceFeatures.h +++ b/src/Magnum/Vk/DeviceFeatures.h @@ -876,81 +876,95 @@ enum class DeviceFeature: UnsignedShort { ShaderStorageTexelBufferArrayNonUniformIndexing, /** - * Whether uniform buffer descriptors can be updated after a set is bound. + * Whether @ref DescriptorType::UniformBuffer / + * @relativeref{DescriptorType,UniformBufferDynamic} can be updated after a + * set is bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingSampledImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingUniformBufferUpdateAfterBind, /** - * Whether sampled image descriptors can be updated after a set is bound. + * Whether @ref DescriptorType::CombinedImageSampler / + * @relativeref{DescriptorType,SampledImage} can be updated after a set is + * bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingUniformBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingSampledImageUpdateAfterBind, /** - * Whether storage image descriptors can be updated after a set is bound. + * Whether @ref DescriptorType::StorageImage can be updated after a set is + * bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingUniformBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingSampledImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingStorageImageUpdateAfterBind, /** - * Whether storage buffer descriptors can be updated after a set is bound. + * Whether @ref DescriptorType::StorageBuffer / + * @relativeref{DescriptorType,StorageBufferDynamic} can be updated after a + * set is bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingUniformBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingSampledImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingStorageBufferUpdateAfterBind, /** - * Whether uniform texel buffer descriptors can be updated after a set is - * bound. + * Whether @ref DescriptorType::UniformTexelBuffer can be updated after a + * set is bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingUniformBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingSampledImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingStorageTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingUniformTexelBufferUpdateAfterBind, /** - * Whether storage texel buffer descriptors can be updated after a set is - * bound. + * Whether @ref DescriptorType::StorageTexelBuffer can be updated after a + * set is bound. * @see @ref DeviceFeature::DescriptorBindingAccelerationStructureUpdateAfterBind, * @relativeref{DeviceFeature,DescriptorBindingUniformBufferUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingSampledImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageImageUpdateAfterBind}, * @relativeref{DeviceFeature,DescriptorBindingStorageBufferUpdateAfterBind}, - * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind} + * @relativeref{DeviceFeature,DescriptorBindingUniformTexelBufferUpdateAfterBind}, + * @ref DescriptorSetLayoutBinding::Flag::UpdateAfterBind * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingStorageTexelBufferUpdateAfterBind, /** * Whether descriptors can be updated while the set is in use. + * @see @ref DescriptorSetLayoutBinding::Flag::UpdateUnusedWhilePending * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingUpdateUnusedWhilePending, @@ -958,6 +972,7 @@ enum class DeviceFeature: UnsignedShort { /** * Whether a descriptor set binding in which some descriptors are not valid * can be statically used. + * @see @ref DescriptorSetLayoutBinding::Flag::PartiallyBound * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingPartiallyBound, @@ -965,6 +980,7 @@ enum class DeviceFeature: UnsignedShort { /** * Whether descriptor sets with a variably-sized last binding are * supported. + * @see @ref DescriptorSetLayoutBinding::Flag::VariableDescriptorCount * @requires_vk12 Extension @vk_extension{EXT,descriptor_indexing} */ DescriptorBindingVariableDescriptorCount, diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 138646647..3b831485c 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -133,6 +133,9 @@ enum class ImageLayout: Int { * * Only valid for images created with @ref ImageUsage::Sampled or * @ref ImageUsage::InputAttachment. + * @see @ref DescriptorType::CombinedImageSampler, + * @ref DescriptorType::SampledImage, + * @ref DescriptorType::InputAttachment */ ShaderReadOnly = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, diff --git a/src/Magnum/Vk/ImageCreateInfo.h b/src/Magnum/Vk/ImageCreateInfo.h index 0e0a656a2..02ed20291 100644 --- a/src/Magnum/Vk/ImageCreateInfo.h +++ b/src/Magnum/Vk/ImageCreateInfo.h @@ -82,7 +82,8 @@ enum class ImageUsage: UnsignedInt { * * Not all pixel formats support sampling, see @ref PixelFormat for more * information. - * @see @ref ImageLayout::ShaderReadOnly + * @see @ref ImageLayout::ShaderReadOnly, + * @ref DescriptorType::SampledImage */ Sampled = VK_IMAGE_USAGE_SAMPLED_BIT, @@ -92,7 +93,8 @@ enum class ImageUsage: UnsignedInt { * Not all pixel formats support shader storage, with some requiring the * @ref DeviceFeature::ShaderStorageImageExtendedFormats feature. See * @ref PixelFormat for more information. - * @see @ref ImageLayout::General + * @see @ref ImageLayout::General, + * @ref DescriptorType::StorageImage */ Storage = VK_IMAGE_USAGE_STORAGE_BIT, @@ -125,6 +127,7 @@ enum class ImageUsage: UnsignedInt { * Input attachment in a shader or framebuffer * * @see @ref ImageLayout::ShaderReadOnly, + * @ref DescriptorType::InputAttachment, * @ref SubpassDescription::setInputAttachments() */ InputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 7738e9936..04ca80668 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -27,6 +27,7 @@ corrade_add_test(VkBufferTest BufferTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkCommandBufferTest CommandBufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkDescriptorSetLayoutTest DescriptorSetLayoutTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceFeaturesTest DeviceFeaturesTest.cpp LIBRARIES MagnumVk) @@ -129,6 +130,7 @@ set_target_properties( VkBufferTest VkCommandBufferTest VkCommandPoolTest + VkDescriptorSetLayoutTest VkDeviceTest VkDeviceFeaturesTest VkDevicePropertiesTest @@ -192,6 +194,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) + corrade_add_test(VkDescriptorSetLayoutVkTest DescriptorSetLayoutVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) @@ -243,6 +246,7 @@ if(BUILD_VK_TESTS) VkBufferVkTest VkCommandBufferVkTest VkCommandPoolVkTest + VkDescriptorSetLayoutVkTest VkDeviceVkTest VkDevicePropertiesVkTest VkExtensionPropertiesVkTest diff --git a/src/Magnum/Vk/Test/DescriptorSetLayoutTest.cpp b/src/Magnum/Vk/Test/DescriptorSetLayoutTest.cpp new file mode 100644 index 000000000..750d41128 --- /dev/null +++ b/src/Magnum/Vk/Test/DescriptorSetLayoutTest.cpp @@ -0,0 +1,320 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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 "Magnum/Vk/DescriptorSetLayoutCreateInfo.h" +#include "Magnum/Vk/Shader.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DescriptorSetLayoutTest: TestSuite::Tester { + explicit DescriptorSetLayoutTest(); + + void bindingConstruct(); + void bindingConstructCountStages(); + void bindingConstructFlags(); + void bindingConstructImmutableSamplers(); + void bindingConstructNoInit(); + void bindingConstructFromVk(); + void bindingConstructCopy(); + void bindingConstructMove(); + + void createInfoConstruct(); + void createInfoConstructBindingFlags(); + void createInfoConstructBindingImmutableSamplers(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + void createInfoConstructCopy(); + void createInfoConstructMove(); + + void constructNoCreate(); + void constructCopy(); +}; + +DescriptorSetLayoutTest::DescriptorSetLayoutTest() { + addTests({&DescriptorSetLayoutTest::bindingConstruct, + &DescriptorSetLayoutTest::bindingConstructCountStages, + &DescriptorSetLayoutTest::bindingConstructFlags, + &DescriptorSetLayoutTest::bindingConstructImmutableSamplers, + &DescriptorSetLayoutTest::bindingConstructNoInit, + &DescriptorSetLayoutTest::bindingConstructFromVk, + &DescriptorSetLayoutTest::bindingConstructCopy, + &DescriptorSetLayoutTest::bindingConstructMove, + + &DescriptorSetLayoutTest::createInfoConstruct, + &DescriptorSetLayoutTest::createInfoConstructBindingFlags, + &DescriptorSetLayoutTest::createInfoConstructBindingImmutableSamplers, + &DescriptorSetLayoutTest::createInfoConstructNoInit, + &DescriptorSetLayoutTest::createInfoConstructFromVk, + &DescriptorSetLayoutTest::createInfoConstructCopy, + &DescriptorSetLayoutTest::createInfoConstructMove, + + &DescriptorSetLayoutTest::constructNoCreate, + &DescriptorSetLayoutTest::constructCopy}); +} + +void DescriptorSetLayoutTest::bindingConstruct() { + DescriptorSetLayoutBinding binding{15, DescriptorType::SampledImage}; + CORRADE_COMPARE(binding->binding, 15); + CORRADE_COMPARE(binding->descriptorType, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + CORRADE_COMPARE(binding->descriptorCount, 1); + CORRADE_VERIFY(!binding->pImmutableSamplers); + CORRADE_COMPARE(binding->stageFlags, VK_SHADER_STAGE_ALL); + CORRADE_COMPARE(binding.flags(), 0); +} + +void DescriptorSetLayoutTest::bindingConstructCountStages() { + DescriptorSetLayoutBinding binding{15, DescriptorType::SampledImage, 5, ShaderStage::Fragment|ShaderStage::RayMiss}; + CORRADE_COMPARE(binding->binding, 15); + CORRADE_COMPARE(binding->descriptorType, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + CORRADE_COMPARE(binding->descriptorCount, 5); + CORRADE_VERIFY(!binding->pImmutableSamplers); + CORRADE_COMPARE(binding->stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT|VK_SHADER_STAGE_MISS_BIT_KHR); + CORRADE_COMPARE(binding.flags(), 0); +} + +void DescriptorSetLayoutTest::bindingConstructFlags() { + DescriptorSetLayoutBinding binding{15, DescriptorType::SampledImage, 2, ShaderStage::Fragment, DescriptorSetLayoutBinding::Flag::UpdateAfterBind|DescriptorSetLayoutBinding::Flag::PartiallyBound}; + CORRADE_COMPARE(binding->binding, 15); + CORRADE_COMPARE(binding->descriptorType, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + CORRADE_COMPARE(binding->descriptorCount, 2); + CORRADE_VERIFY(!binding->pImmutableSamplers); + CORRADE_COMPARE(binding->stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT); + CORRADE_COMPARE(binding.flags(), VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT|VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT); +} + +void DescriptorSetLayoutTest::bindingConstructImmutableSamplers() { + DescriptorSetLayoutBinding binding{15, DescriptorType::SampledImage, {reinterpret_cast(0xdead), reinterpret_cast(0xbeef), reinterpret_cast(0xcafe)}, ShaderStage::Fragment, DescriptorSetLayoutBinding::Flag::UpdateAfterBind}; + CORRADE_COMPARE(binding->binding, 15); + CORRADE_COMPARE(binding->descriptorType, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + CORRADE_COMPARE(binding->descriptorCount, 3); + CORRADE_VERIFY(binding->pImmutableSamplers); + CORRADE_COMPARE(binding->pImmutableSamplers[0], reinterpret_cast(0xdead)); + CORRADE_COMPARE(binding->pImmutableSamplers[1], reinterpret_cast(0xbeef)); + CORRADE_COMPARE(binding->pImmutableSamplers[2], reinterpret_cast(0xcafe)); + CORRADE_COMPARE(binding->stageFlags, VK_SHADER_STAGE_FRAGMENT_BIT); + CORRADE_COMPARE(binding.flags(), VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT); +} + +void DescriptorSetLayoutTest::bindingConstructNoInit() { + VkSampler a; + DescriptorSetLayoutBinding info{NoInit}; + info->pImmutableSamplers = &a; + new(&info) DescriptorSetLayoutBinding{NoInit}; + CORRADE_COMPARE(info->pImmutableSamplers, &a); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DescriptorSetLayoutTest::bindingConstructFromVk() { + VkSampler a; + + VkDescriptorSetLayoutBinding vkInfo; + vkInfo.pImmutableSamplers = &a; + + DescriptorSetLayoutBinding info{vkInfo}; + CORRADE_COMPARE(info->pImmutableSamplers, &a); +} + +void DescriptorSetLayoutTest::bindingConstructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +void DescriptorSetLayoutTest::bindingConstructMove() { + DescriptorSetLayoutBinding a{15, DescriptorType::SampledImage, {reinterpret_cast(0xdead), reinterpret_cast(0xbeef), reinterpret_cast(0xcafe)}, ShaderStage::Fragment, DescriptorSetLayoutBinding::Flag::UpdateAfterBind}; + CORRADE_COMPARE(a->descriptorCount, 3); + CORRADE_VERIFY(a->pImmutableSamplers); + CORRADE_COMPARE(a->pImmutableSamplers[1], reinterpret_cast(0xbeef)); + + DescriptorSetLayoutBinding b = std::move(a); + CORRADE_VERIFY(!a->pImmutableSamplers); + CORRADE_COMPARE(b->descriptorCount, 3); + CORRADE_VERIFY(b->pImmutableSamplers); + CORRADE_COMPARE(b->pImmutableSamplers[1], reinterpret_cast(0xbeef)); + CORRADE_COMPARE(b.flags(), VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT); + + DescriptorSetLayoutBinding c{3, {}}; + c = std::move(b); + CORRADE_VERIFY(!b->pImmutableSamplers); + CORRADE_COMPARE(c->descriptorCount, 3); + CORRADE_VERIFY(c->pImmutableSamplers); + CORRADE_COMPARE(c->pImmutableSamplers[1], reinterpret_cast(0xbeef)); + CORRADE_COMPARE(c.flags(), VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT); +} + +void DescriptorSetLayoutTest::createInfoConstruct() { + DescriptorSetLayoutCreateInfo info{{ + /* I hope the {{ will no longer be needed with C++14? */ + {{7, DescriptorType::UniformBuffer}}, + {{12, DescriptorType::CombinedImageSampler}} + }, DescriptorSetLayoutCreateInfo::Flag(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT)}; + CORRADE_COMPARE(info->bindingCount, 2); + CORRADE_VERIFY(info->pBindings); + CORRADE_COMPARE(info->pBindings[0].binding, 7); + CORRADE_COMPARE(info->pBindings[0].descriptorType, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + CORRADE_COMPARE(info->pBindings[0].descriptorCount, 1); + CORRADE_COMPARE(info->pBindings[1].binding, 12); + CORRADE_COMPARE(info->pBindings[1].descriptorType, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + CORRADE_COMPARE(info->pBindings[1].descriptorCount, 1); + CORRADE_COMPARE(info->flags, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT); + CORRADE_VERIFY(!info->pNext); +} + +void DescriptorSetLayoutTest::createInfoConstructBindingFlags() { + DescriptorSetLayoutCreateInfo info{{ + {{7, DescriptorType::UniformBuffer}}, + {{12, DescriptorType::CombinedImageSampler, 1, ~ShaderStages{}, DescriptorSetLayoutBinding::Flag::PartiallyBound}} + }}; + CORRADE_COMPARE(info->bindingCount, 2); + CORRADE_VERIFY(info->pBindings); + CORRADE_COMPARE(info->pBindings[0].binding, 7); + CORRADE_COMPARE(info->pBindings[0].descriptorType, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + CORRADE_COMPARE(info->pBindings[0].descriptorCount, 1); + CORRADE_COMPARE(info->pBindings[1].binding, 12); + CORRADE_COMPARE(info->pBindings[1].descriptorType, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + CORRADE_COMPARE(info->pBindings[1].descriptorCount, 1); + + CORRADE_VERIFY(info->pNext); + const auto& flags = *static_cast(info->pNext); + CORRADE_COMPARE(flags.bindingCount, 2); + CORRADE_VERIFY(flags.pBindingFlags); + CORRADE_COMPARE(flags.pBindingFlags[0], 0); + CORRADE_COMPARE(flags.pBindingFlags[1], VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT); +} + +void DescriptorSetLayoutTest::createInfoConstructBindingImmutableSamplers() { + DescriptorSetLayoutBinding binding{3, DescriptorType::Sampler, {reinterpret_cast(0xdead), reinterpret_cast(0xbeef)}}; + + DescriptorSetLayoutCreateInfo info{{ + {{7, DescriptorType::UniformBuffer}}, + binding, + {{12, DescriptorType::CombinedImageSampler, {reinterpret_cast(0xcafe)}}}, + }}; + CORRADE_COMPARE(info->bindingCount, 3); + CORRADE_VERIFY(info->pBindings); + + CORRADE_COMPARE(info->pBindings[0].binding, 7); + CORRADE_COMPARE(info->pBindings[0].descriptorType, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + CORRADE_COMPARE(info->pBindings[0].descriptorCount, 1); + CORRADE_VERIFY(!info->pBindings[0].pImmutableSamplers); + + CORRADE_COMPARE(info->pBindings[1].binding, 3); + CORRADE_COMPARE(info->pBindings[1].descriptorType, VK_DESCRIPTOR_TYPE_SAMPLER); + CORRADE_COMPARE(info->pBindings[1].descriptorCount, 2); + CORRADE_VERIFY(info->pBindings[1].pImmutableSamplers); + /* The samplers should get copied, not referenced */ + CORRADE_VERIFY(info->pBindings[1].pImmutableSamplers != binding->pImmutableSamplers); + CORRADE_COMPARE(info->pBindings[1].pImmutableSamplers[0], reinterpret_cast(0xdead)); + CORRADE_COMPARE(info->pBindings[1].pImmutableSamplers[1], reinterpret_cast(0xbeef)); + + CORRADE_COMPARE(info->pBindings[2].binding, 12); + CORRADE_COMPARE(info->pBindings[2].descriptorType, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + CORRADE_COMPARE(info->pBindings[2].descriptorCount, 1); + CORRADE_VERIFY(info->pBindings[2].pImmutableSamplers); + CORRADE_COMPARE(info->pBindings[2].pImmutableSamplers[0], reinterpret_cast(0xcafe)); + + CORRADE_VERIFY(!info->pNext); +} + +void DescriptorSetLayoutTest::createInfoConstructNoInit() { + DescriptorSetLayoutCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) DescriptorSetLayoutCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DescriptorSetLayoutTest::createInfoConstructFromVk() { + VkDescriptorSetLayoutCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + DescriptorSetLayoutCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void DescriptorSetLayoutTest::createInfoConstructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +void DescriptorSetLayoutTest::createInfoConstructMove() { + DescriptorSetLayoutCreateInfo a{{ + {{7, DescriptorType::UniformBuffer}}, + {{12, DescriptorType::CombinedImageSampler, 1, ~ShaderStages{}, DescriptorSetLayoutBinding::Flag::PartiallyBound}} + }}; + CORRADE_COMPARE(a->bindingCount, 2); + CORRADE_VERIFY(a->pBindings); + CORRADE_VERIFY(a->pNext); + + DescriptorSetLayoutCreateInfo b = std::move(a); + CORRADE_COMPARE(a->bindingCount, 0); + CORRADE_VERIFY(!a->pBindings); + CORRADE_VERIFY(!a->pNext); + CORRADE_VERIFY(b->pBindings); + CORRADE_VERIFY(b->pNext); + CORRADE_COMPARE(b->pBindings[1].binding, 12); + CORRADE_VERIFY(static_cast(b->pNext)->pBindingFlags); + CORRADE_COMPARE(static_cast(b->pNext)->pBindingFlags[1], VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT); + + DescriptorSetLayoutCreateInfo c{VkDescriptorSetLayoutCreateInfo{}}; + c = std::move(b); + CORRADE_COMPARE(b->bindingCount, 0); + CORRADE_VERIFY(!b->pBindings); + CORRADE_VERIFY(!b->pNext); + CORRADE_VERIFY(c->pBindings); + CORRADE_VERIFY(c->pNext); + CORRADE_COMPARE(c->pBindings[1].binding, 12); + CORRADE_VERIFY(static_cast(c->pNext)->pBindingFlags); + CORRADE_COMPARE(static_cast(c->pNext)->pBindingFlags[1], VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT); +} + +void DescriptorSetLayoutTest::constructNoCreate() { + { + DescriptorSetLayout fence{NoCreate}; + CORRADE_VERIFY(!fence.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DescriptorSetLayoutTest::constructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DescriptorSetLayoutTest) diff --git a/src/Magnum/Vk/Test/DescriptorSetLayoutVkTest.cpp b/src/Magnum/Vk/Test/DescriptorSetLayoutVkTest.cpp new file mode 100644 index 000000000..ca463eccc --- /dev/null +++ b/src/Magnum/Vk/Test/DescriptorSetLayoutVkTest.cpp @@ -0,0 +1,104 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 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 "Magnum/Vk/DescriptorSetLayoutCreateInfo.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DescriptorSetLayoutVkTest: VulkanTester { + explicit DescriptorSetLayoutVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +DescriptorSetLayoutVkTest::DescriptorSetLayoutVkTest() { + addTests({&DescriptorSetLayoutVkTest::construct, + &DescriptorSetLayoutVkTest::constructMove, + + &DescriptorSetLayoutVkTest::wrap}); +} + +void DescriptorSetLayoutVkTest::construct() { + { + DescriptorSetLayout layout{device(), DescriptorSetLayoutCreateInfo{ + {{15, DescriptorType::UniformBuffer}} + }}; + CORRADE_VERIFY(layout.handle()); + CORRADE_COMPARE(layout.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void DescriptorSetLayoutVkTest::constructMove() { + DescriptorSetLayout a{device(), DescriptorSetLayoutCreateInfo{ + {{15, DescriptorType::UniformBuffer}} + }}; + VkDescriptorSetLayout handle = a.handle(); + + DescriptorSetLayout b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + DescriptorSetLayout 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 DescriptorSetLayoutVkTest::wrap() { + VkDescriptorSetLayout layout{}; + CORRADE_COMPARE(Result(device()->CreateDescriptorSetLayout(device(), + DescriptorSetLayoutCreateInfo{ + {{15, DescriptorType::UniformBuffer}} + }, + nullptr, &layout)), Result::Success); + + auto wrapped = DescriptorSetLayout::wrap(device(), layout, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), layout); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), layout); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyDescriptorSetLayout(device(), layout, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DescriptorSetLayoutVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 68495270d..33b453d64 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -57,6 +57,9 @@ class CopyImageToBufferInfo; /* Not forward-declaring CopyBufferToImageInfo1D etc right now, I see no need */ enum class DependencyFlag: UnsignedInt; typedef Containers::EnumSet DependencyFlags; +class DescriptorSetLayout; +class DescriptorSetLayoutCreateInfo; +enum class DescriptorType: Int; class Device; class DeviceCreateInfo; enum class DeviceFeature: UnsignedShort;