From aae41288e8e96f7c44512e8798ffdd25461d10b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 19 Jan 2021 13:04:41 +0100 Subject: [PATCH] Vk: implement a pipeline barrier command. --- doc/vulkan-mapping.dox | 8 +- src/Magnum/Vk/CMakeLists.txt | 1 + src/Magnum/Vk/CommandBuffer.h | 37 +++++ src/Magnum/Vk/Pipeline.cpp | 101 +++++++++++++ src/Magnum/Vk/Pipeline.h | 205 +++++++++++++++++++++++++- src/Magnum/Vk/RenderPassCreateInfo.h | 3 + src/Magnum/Vk/Test/CMakeLists.txt | 4 + src/Magnum/Vk/Test/PipelineTest.cpp | 155 +++++++++++++++++++ src/Magnum/Vk/Test/PipelineVkTest.cpp | 78 ++++++++++ src/Magnum/Vk/Vk.h | 3 + 10 files changed, 590 insertions(+), 5 deletions(-) create mode 100644 src/Magnum/Vk/Pipeline.cpp create mode 100644 src/Magnum/Vk/Test/PipelineTest.cpp create mode 100644 src/Magnum/Vk/Test/PipelineVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 07890b3fa..170f76d99 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -144,7 +144,7 @@ Vulkan function | Matching API @fn_vk{CmdExecuteCommands} | | @fn_vk{CmdFillBuffer} | | @fn_vk{CmdInsertDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | -@fn_vk{CmdPipelineBarrier} | | +@fn_vk{CmdPipelineBarrier} | @ref CommandBuffer::pipelineBarrier() @fn_vk{CmdPushConstants} | | @fn_vk{CmdResetEvent} | | @fn_vk{CmdResetQueryPool} | | @@ -404,7 +404,7 @@ Vulkan structure | Matching API @type_vk{BufferCreateInfo} | @ref BufferCreateInfo @type_vk{BufferDeviceAddressInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{BufferImageCopy}, \n @type_vk{BufferImageCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{BufferMemoryBarrier} | | +@type_vk{BufferMemoryBarrier} | @ref BufferMemoryBarrier @type_vk{BufferMemoryRequirementsInfo}, \n @type_vk{BufferMemoryRequirementsInfo2} @m_class{m-label m-flat m-success} **KHR, 1.1** | not exposed, internal to @ref Buffer::memoryRequirements() @type_vk{BufferOpaqueCaptureAddressCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{BufferViewCreateInfo} | | @@ -533,7 +533,7 @@ Vulkan structure | Matching API @type_vk{ImageFormatListCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{ImageFormatProperties}, \n @type_vk{ImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{ImageSubresourceRange} | not exposed, internal to @ref ImageViewCreateInfo -@type_vk{ImageMemoryBarrier} | | +@type_vk{ImageMemoryBarrier} | @ref ImageMemoryBarrier @type_vk{ImageMemoryRequirementsInfo}, \n @type_vk{ImageMemoryRequirementsInfo2} @m_class{m-label m-flat m-success} **KHR, 1.1** | not exposed, internal to @ref Image::memoryRequirements() @type_vk{ImagePlaneMemoryRequirementsInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{ImageResolve}, \n @type_vk{ImageResolve2KHR} @m_class{m-label m-flat m-warning} **KHR** | | @@ -561,7 +561,7 @@ Vulkan structure | Matching API @type_vk{MappedMemoryRange} | | @type_vk{MemoryAllocateInfo} | @ref MemoryAllocateInfo @type_vk{MemoryAllocateFlagsInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@type_vk{MemoryBarrier} | | +@type_vk{MemoryBarrier} | @ref MemoryBarrier @type_vk{MemoryDedicatedAllocateInfo} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{MemoryDedicatedRequirements} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{MemoryHeap} | @ref DeviceProperties::memoryHeapSize(), \n @ref DeviceProperties::memoryHeapFlags() diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index cdb8c9710..4bb367beb 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -33,6 +33,7 @@ set(MagnumVk_SRCS Fence.cpp Framebuffer.cpp Handle.cpp + Pipeline.cpp Queue.cpp Result.cpp Shader.cpp diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index a8588f31d..181caec19 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -30,6 +30,7 @@ * @m_since_latest */ +#include #include #include "Magnum/Tags.h" @@ -356,6 +357,42 @@ class MAGNUM_VK_EXPORT CommandBuffer { CommandBuffer& endRenderPass(); #endif + /** + * @brief Insert a memory dependency + * @param sourceStages Source stages. Has to contain at least + * one stage. + * @param destinationStages Destination stages. Has to contain at + * least one stage. + * @param memoryBarriers Global memory barriers, affecting all + * memory + * @param bufferMemoryBarriers Buffer memory barriers, restricted to + * a particular buffer + * @param imageMemoryBarriers Image memory barriers, restricted to a + * particular image. These are also used for performing + * @ref ImageLayout transitions. + * @param dependencyFlags Dependency flags + * @return Reference to self (for method chaining) + * + * Can be called both inside and outside a render pass. When called + * inside a render pass: + * + * - the render pass has to contain at least one + * @ref SubpassDependency to itself, with scopes that are a + * superset of scopes defined here + * - @p bufferMemoryBarriers has to be empty, + * - all images in @p imageMemoryBarriers have to be contained + * in both @ref SubpassDescription::setInputAttachments() and + * @ref SubpassDescription::setColorAttachments() / + * @ref SubpassDescription::setDepthStencilAttachment(), + * - and old and new @ref ImageLayout in @p imageMemoryBarriers have + * to be equal + * + * @see @fn_vk_keyword{CmdPipelineBarrier} + */ + CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, Containers::ArrayView memoryBarriers, Containers::ArrayView bufferMemoryBarriers, Containers::ArrayView imageMemoryBarriers, DependencyFlags dependencyFlags = {}); + /** @overload */ + CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, std::initializer_list memoryBarriers, std::initializer_list bufferMemoryBarriers, std::initializer_list imageMemoryBarriers, DependencyFlags dependencyFlags = {}); + private: friend CommandPool; friend Implementation::DeviceState; diff --git a/src/Magnum/Vk/Pipeline.cpp b/src/Magnum/Vk/Pipeline.cpp new file mode 100644 index 000000000..d942c204c --- /dev/null +++ b/src/Magnum/Vk/Pipeline.cpp @@ -0,0 +1,101 @@ +/* + 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 "Pipeline.h" +#include "CommandBuffer.h" + +#include + +#include "Magnum/Vk/Device.h" + +namespace Magnum { namespace Vk { + +MemoryBarrier::MemoryBarrier(const Accesses sourceAccesses, const Accesses destinationAccesses): _barrier{} { + _barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + _barrier.srcAccessMask = VkAccessFlags(sourceAccesses); + _barrier.dstAccessMask = VkAccessFlags(destinationAccesses); +} + +MemoryBarrier::MemoryBarrier(NoInitT) noexcept {} + +MemoryBarrier::MemoryBarrier(const VkMemoryBarrier& barrier): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _barrier(barrier) {} + +BufferMemoryBarrier::BufferMemoryBarrier(const Accesses sourceAccesses, const Accesses destinationAccesses, const VkBuffer buffer, const UnsignedLong offset, const UnsignedLong size): _barrier{} { + _barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + _barrier.srcAccessMask = VkAccessFlags(sourceAccesses); + _barrier.dstAccessMask = VkAccessFlags(destinationAccesses); + _barrier.buffer = buffer; + _barrier.offset = offset; + _barrier.size = size; +} + +BufferMemoryBarrier::BufferMemoryBarrier(NoInitT) noexcept {} + +BufferMemoryBarrier::BufferMemoryBarrier(const VkBufferMemoryBarrier& barrier): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _barrier(barrier) {} + +ImageMemoryBarrier::ImageMemoryBarrier(const Accesses sourceAccesses, const Accesses destinationAccesses, const ImageLayout oldLayout, const ImageLayout newLayout, const VkImage image, const ImageAspects aspects, const UnsignedInt layerOffset, const UnsignedInt layerCount, const UnsignedInt levelOffset, const UnsignedInt levelCount): _barrier{} { + _barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + _barrier.srcAccessMask = VkAccessFlags(sourceAccesses); + _barrier.dstAccessMask = VkAccessFlags(destinationAccesses); + _barrier.oldLayout = VkImageLayout(oldLayout); + _barrier.newLayout = VkImageLayout(newLayout); + _barrier.image = image; + _barrier.subresourceRange.aspectMask = VkImageAspectFlags(aspects); + _barrier.subresourceRange.baseMipLevel = levelOffset; + _barrier.subresourceRange.levelCount = levelCount; + _barrier.subresourceRange.baseArrayLayer = layerOffset; + _barrier.subresourceRange.layerCount = layerCount; +} + +ImageMemoryBarrier::ImageMemoryBarrier(NoInitT) noexcept {} + +ImageMemoryBarrier::ImageMemoryBarrier(const VkImageMemoryBarrier& barrier): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _barrier(barrier) {} + +CommandBuffer& CommandBuffer::pipelineBarrier(const PipelineStages sourceStages, const PipelineStages destinationStages, const Containers::ArrayView memoryBarriers, const Containers::ArrayView bufferMemoryBarriers, const Containers::ArrayView imageMemoryBarriers, const DependencyFlags dependencyFlags) { + /* Once these grow (VkSampleLocationsInfoEXT?), they will need to be + linearized into a separate array first */ + static_assert( + sizeof(MemoryBarrier) == sizeof(VkMemoryBarrier) && + sizeof(BufferMemoryBarrier) == sizeof(VkBufferMemoryBarrier) && + sizeof(ImageMemoryBarrier) == sizeof(VkImageMemoryBarrier), + ""); + (**_device).CmdPipelineBarrier(_handle, VkPipelineStageFlags(sourceStages), VkPipelineStageFlags(destinationStages), VkDependencyFlags(dependencyFlags), memoryBarriers.size(), memoryBarriers[0], bufferMemoryBarriers.size(), bufferMemoryBarriers[0], imageMemoryBarriers.size(), imageMemoryBarriers[0]); + return *this; +} + +CommandBuffer& CommandBuffer::pipelineBarrier(const PipelineStages sourceStages, const PipelineStages destinationStages, const std::initializer_list memoryBarriers, const std::initializer_list bufferMemoryBarriers, const std::initializer_list imageMemoryBarriers, const DependencyFlags dependencyFlags) { + return pipelineBarrier(sourceStages, destinationStages, Containers::arrayView(memoryBarriers), Containers::arrayView(bufferMemoryBarriers), Containers::arrayView(imageMemoryBarriers), dependencyFlags); +} + +}} diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index 3f31c6ef4..065001509 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -26,13 +26,15 @@ */ /** @file - * @brief Enum @ref Magnum::Vk::PipelineStage, @ref Magnum::Vk::Access, @ref Magnum::Vk::DependencyFlag, enum set @ref Magnum::Vk::PipelineStages, @ref Magnum::Vk::Accesses, @ref Magnum::Vk::DependencyFlags + * @brief Class @ref Magnum::Vk::MemoryBarrier, @ref Magnum::Vk::BufferMemoryBarrier, @ref Magnum::Vk::ImageMemoryBarrier, enum @ref Magnum::Vk::PipelineStage, @ref Magnum::Vk::Access, @ref Magnum::Vk::DependencyFlag, enum set @ref Magnum::Vk::PipelineStages, @ref Magnum::Vk::Accesses, @ref Magnum::Vk::DependencyFlags * @m_since_latest */ #include #include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/visibility.h" #include "Magnum/Vk/Vk.h" #include "Magnum/Vk/Vulkan.h" @@ -394,6 +396,207 @@ typedef Containers::EnumSet DependencyFlags; CORRADE_ENUMSET_OPERATORS(DependencyFlags) +/** +@brief Global memory barrier +@m_since_latest + +Wraps a @type_vk_keyword{MemoryBarrier}. This class is subsequently used in +@ref CommandBuffer::pipelineBarrier(). +*/ +class MAGNUM_VK_EXPORT MemoryBarrier { + public: + /** + * @brief Constructor + * @param sourceAccesses Source memory access types + * participating in a dependency + * @param destinationAccesses Destination memory access types + * participating in a dependency + * + * The following @type_vk{MemoryBarrier} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `srcAccessMask` to @p sourceAccesses + * - `dstAccessMask` to @p destinationAccesses + */ + /*implicit*/ MemoryBarrier(Accesses sourceAccesses, Accesses destinationAccesses); + + /** + * @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 MemoryBarrier(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 MemoryBarrier(const VkMemoryBarrier& barrier); + + /** @brief Underlying @type_vk{MemoryBarrier} structure */ + VkMemoryBarrier& operator*() { return _barrier; } + /** @overload */ + const VkMemoryBarrier& operator*() const { return _barrier; } + /** @overload */ + VkMemoryBarrier* operator->() { return &_barrier; } + /** @overload */ + const VkMemoryBarrier* operator->() const { return &_barrier; } + /** @overload */ + operator const VkMemoryBarrier*() const { return &_barrier; } + + private: + VkMemoryBarrier _barrier; +}; + +/** +@brief Memory barrier affecting a single buffer +@m_since_latest + +Wraps a @type_vk_keyword{BufferMemoryBarrier}. Compared to @ref MemoryBarrier +only affects a single buffer. This class is subsequently used in +@ref CommandBuffer::pipelineBarrier(). +*/ +class MAGNUM_VK_EXPORT BufferMemoryBarrier { + public: + /** + * @brief Constructor + * @param sourceAccesses Source memory access types + * participating in a dependency + * @param destinationAccesses Destination memory access types + * participating in a dependency + * @param buffer A @ref Buffer or a raw Vulkan buffer + * handle affected by the barrier + * @param offset Buffer memory offset affected by the + * barrier, in bytes + * @param size Buffer memory size affected by the + * barrier, in bytes + * + * The following @type_vk{BufferMemoryBarrier} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `srcAccessMask` to @p sourceAccesses + * - `dstAccessMask` to @p destinationAccesses + * - `buffer` + * - `offset` + * - `size` + */ + /*implicit*/ BufferMemoryBarrier(Accesses sourceAccesses, Accesses destinationAccesses, VkBuffer buffer, UnsignedLong offset = 0, UnsignedLong size = VK_WHOLE_SIZE); + + /** + * @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 BufferMemoryBarrier(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 BufferMemoryBarrier(const VkBufferMemoryBarrier& barrier); + + /** @brief Underlying @type_vk{BufferMemoryBarrier} structure */ + VkBufferMemoryBarrier& operator*() { return _barrier; } + /** @overload */ + const VkBufferMemoryBarrier& operator*() const { return _barrier; } + /** @overload */ + VkBufferMemoryBarrier* operator->() { return &_barrier; } + /** @overload */ + const VkBufferMemoryBarrier* operator->() const { return &_barrier; } + /** @overload */ + operator const VkBufferMemoryBarrier*() const { return &_barrier; } + + private: + VkBufferMemoryBarrier _barrier; +}; + +/** +@brief Memory barrier affecting a single image +@m_since_latest + +Wraps a @type_vk_keyword{ImageMemoryBarrier}. Compared to @ref MemoryBarrier +only affects a single image and additionally performs @ref ImageLayout +transitions. This class is subsequently used in +@ref CommandBuffer::pipelineBarrier(). +*/ +class MAGNUM_VK_EXPORT ImageMemoryBarrier { + public: + /** + * @brief Constructor + * @param sourceAccesses Source memory access types + * participating in a dependency + * @param oldLayout Old layout in an image layout + * transition + * @param destinationAccesses Destination memory access types + * participating in a dependency + * @param newLayout New layout in an image layout + * transition + * @param image An @ref Image or a raw Vulkan image + * handle affected by the barrier + * @param aspects Image aspects affected by the barrier + * @param layerOffset Offset to the first layer affected by + * the barrier + * @param layerCount Layer count affected by the barrier + * @param levelOffset Offset to the first mip level affected + * by the barrier + * @param levelCount Mip level count affected by the barrier + * + * The following @type_vk{ImageMemoryBarrier} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `srcAccessMask` to @p sourceAccesses + * - `dstAccessMask` to @p destinationAccesses + * - `oldLayout` + * - `newLayout` + * - `image` + * - `subresourceRange.aspectMask` to @p aspects + * - `subresourceRange.levelOffset` to @p levelOffset + * - `subresourceRange.levelCount` to @p levelCount + * - `subresourceRange.baseArrayLayer` to @p layerOffset + * - `subresourceRange.layerCount` to @p layerCount + */ + /*implicit*/ ImageMemoryBarrier(Accesses sourceAccesses, Accesses destinationAccesses, ImageLayout oldLayout, ImageLayout newLayout, VkImage image, ImageAspects aspects, UnsignedInt layerOffset = 0, UnsignedInt layerCount = VK_REMAINING_ARRAY_LAYERS, UnsignedInt levelOffset = 0, UnsignedInt levelCount = VK_REMAINING_MIP_LEVELS); + + /** + * @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 ImageMemoryBarrier(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 ImageMemoryBarrier(const VkImageMemoryBarrier& barrier); + + /** @brief Underlying @type_vk{ImageMemoryBarrier} structure */ + VkImageMemoryBarrier& operator*() { return _barrier; } + /** @overload */ + const VkImageMemoryBarrier& operator*() const { return _barrier; } + /** @overload */ + VkImageMemoryBarrier* operator->() { return &_barrier; } + /** @overload */ + const VkImageMemoryBarrier* operator->() const { return &_barrier; } + /** @overload */ + operator const VkImageMemoryBarrier*() const { return &_barrier; } + + private: + VkImageMemoryBarrier _barrier; +}; + }} #endif diff --git a/src/Magnum/Vk/RenderPassCreateInfo.h b/src/Magnum/Vk/RenderPassCreateInfo.h index c04fc03ae..465aa06ba 100644 --- a/src/Magnum/Vk/RenderPassCreateInfo.h +++ b/src/Magnum/Vk/RenderPassCreateInfo.h @@ -718,6 +718,9 @@ all fields that are present only in @type_vk{SubpassDependency2} --- in particular, the whole `pNext` pointer chain is omitted. When performing the conversion it's your responsibility to ensure nothing significant was in the fields that were left out. + +@see @ref MemoryBarrier, @ref ImageMemoryBarrier, + @ref CommandBuffer::pipelineBarrier() */ class MAGNUM_VK_EXPORT SubpassDependency { public: diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 4552500e0..d17dda6db 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -42,6 +42,7 @@ corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVkTestLib) +corrade_add_test(VkPipelineTest PipelineTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkPixelFormatTest PixelFormatTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkQueueTest QueueTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) @@ -135,6 +136,7 @@ set_target_properties( VkIntegrationTest VkLayerPropertiesTest VkMemoryTest + VkPipelineTest VkPixelFormatTest VkQueueTest VkResultTest @@ -167,6 +169,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkImageViewVkTest ImageViewVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + corrade_add_test(VkPipelineVkTest PipelineVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkQueueVkTest QueueVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkRenderPassVkTest RenderPassVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkShaderVkTest ShaderVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) @@ -187,6 +190,7 @@ if(BUILD_VK_TESTS) VkImageViewVkTest VkInstanceVkTest VkMemoryVkTest + VkPipelineVkTest VkQueueVkTest VkRenderPassVkTest VkShaderVkTest diff --git a/src/Magnum/Vk/Test/PipelineTest.cpp b/src/Magnum/Vk/Test/PipelineTest.cpp new file mode 100644 index 000000000..c5edf0e9d --- /dev/null +++ b/src/Magnum/Vk/Test/PipelineTest.cpp @@ -0,0 +1,155 @@ +/* + 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/Image.h" +#include "Magnum/Vk/Pipeline.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct PipelineTest: TestSuite::Tester { + explicit PipelineTest(); + + void memoryBarrierConstruct(); + void memoryBarrierConstructNoInit(); + void memoryBarrierConstructFromVk(); + + void bufferMemoryBarrierConstruct(); + void bufferMemoryBarrierConstructNoInit(); + void bufferMemoryBarrierConstructFromVk(); + + void imageMemoryBarrierConstruct(); + void imageMemoryBarrierConstructNoInit(); + void imageMemoryBarrierConstructFromVk(); +}; + +PipelineTest::PipelineTest() { + addTests({&PipelineTest::memoryBarrierConstruct, + &PipelineTest::memoryBarrierConstructNoInit, + &PipelineTest::memoryBarrierConstructFromVk, + + &PipelineTest::bufferMemoryBarrierConstruct, + &PipelineTest::bufferMemoryBarrierConstructNoInit, + &PipelineTest::bufferMemoryBarrierConstructFromVk, + + &PipelineTest::imageMemoryBarrierConstruct, + &PipelineTest::imageMemoryBarrierConstructNoInit, + &PipelineTest::imageMemoryBarrierConstructFromVk}); +} + +void PipelineTest::memoryBarrierConstruct() { + MemoryBarrier barrier{Access::ColorAttachmentWrite|Access::DepthStencilAttachmentWrite, Access::TransferRead}; + CORRADE_COMPARE(barrier->srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + CORRADE_COMPARE(barrier->dstAccessMask, VK_ACCESS_TRANSFER_READ_BIT); +} + +void PipelineTest::memoryBarrierConstructNoInit() { + MemoryBarrier barrier{NoInit}; + barrier->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&barrier) MemoryBarrier{NoInit}; + CORRADE_COMPARE(barrier->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 PipelineTest::memoryBarrierConstructFromVk() { + VkMemoryBarrier vkBarrier; + vkBarrier.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + MemoryBarrier barrier{vkBarrier}; + CORRADE_COMPARE(barrier->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void PipelineTest::bufferMemoryBarrierConstruct() { + BufferMemoryBarrier barrier{Access::ColorAttachmentWrite|Access::DepthStencilAttachmentWrite, Access::TransferRead, reinterpret_cast(0xdead), 3, 5}; + CORRADE_COMPARE(barrier->srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + CORRADE_COMPARE(barrier->dstAccessMask, VK_ACCESS_TRANSFER_READ_BIT); + CORRADE_COMPARE(barrier->buffer, reinterpret_cast(0xdead)); + CORRADE_COMPARE(barrier->offset, 3); + CORRADE_COMPARE(barrier->size, 5); +} + +void PipelineTest::bufferMemoryBarrierConstructNoInit() { + BufferMemoryBarrier barrier{NoInit}; + barrier->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&barrier) BufferMemoryBarrier{NoInit}; + CORRADE_COMPARE(barrier->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 PipelineTest::bufferMemoryBarrierConstructFromVk() { + VkBufferMemoryBarrier vkBarrier; + vkBarrier.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + BufferMemoryBarrier barrier{vkBarrier}; + CORRADE_COMPARE(barrier->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void PipelineTest::imageMemoryBarrierConstruct() { + ImageMemoryBarrier barrier{Access::ColorAttachmentWrite|Access::DepthStencilAttachmentWrite, Access::TransferRead, ImageLayout::Preinitialized, ImageLayout::TransferSource, reinterpret_cast(0xdead), ImageAspect::Color|ImageAspect::Depth, 3, 5, 7, 9}; + CORRADE_COMPARE(barrier->srcAccessMask, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + CORRADE_COMPARE(barrier->dstAccessMask, VK_ACCESS_TRANSFER_READ_BIT); + CORRADE_COMPARE(barrier->oldLayout, VK_IMAGE_LAYOUT_PREINITIALIZED); + CORRADE_COMPARE(barrier->newLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + CORRADE_COMPARE(barrier->image, reinterpret_cast(0xdead)); + CORRADE_COMPARE(barrier->subresourceRange.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT|VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(barrier->subresourceRange.baseMipLevel, 7); + CORRADE_COMPARE(barrier->subresourceRange.levelCount, 9); + CORRADE_COMPARE(barrier->subresourceRange.baseArrayLayer, 3); + CORRADE_COMPARE(barrier->subresourceRange.layerCount, 5); +} + +void PipelineTest::imageMemoryBarrierConstructNoInit() { + ImageMemoryBarrier barrier{NoInit}; + barrier->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&barrier) ImageMemoryBarrier{NoInit}; + CORRADE_COMPARE(barrier->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 PipelineTest::imageMemoryBarrierConstructFromVk() { + VkImageMemoryBarrier vkBarrier; + vkBarrier.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + ImageMemoryBarrier barrier{vkBarrier}; + CORRADE_COMPARE(barrier->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::PipelineTest) diff --git a/src/Magnum/Vk/Test/PipelineVkTest.cpp b/src/Magnum/Vk/Test/PipelineVkTest.cpp new file mode 100644 index 000000000..de7c91d51 --- /dev/null +++ b/src/Magnum/Vk/Test/PipelineVkTest.cpp @@ -0,0 +1,78 @@ +/* + 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 "Magnum/Vk/BufferCreateInfo.h" +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPoolCreateInfo.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/ImageCreateInfo.h" +#include "Magnum/Vk/Pipeline.h" +#include "Magnum/Vk/PixelFormat.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct PipelineVkTest: VulkanTester { + explicit PipelineVkTest(); + + void pipelineBarrier(); +}; + +PipelineVkTest::PipelineVkTest() { + addTests({&PipelineVkTest::pipelineBarrier}); +} + +void PipelineVkTest::pipelineBarrier() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + + Buffer buffer{device(), BufferCreateInfo{ + BufferUsage::TransferDestination|BufferUsage::VertexBuffer, 16 + }, MemoryFlag::DeviceLocal}; + Image image{device(), ImageCreateInfo2D{ + ImageUsage::TransferDestination|ImageUsage::Sampled, + PixelFormat::RGBA8Unorm, {4, 4}, 1 + }, MemoryFlag::DeviceLocal}; + + pool.allocate() + .begin() + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host|PipelineStage::VertexInput|PipelineStage::FragmentShader, { + {Access::TransferWrite, Access::HostRead} + }, { + {Access::TransferWrite, Access::VertexAttributeRead, buffer} + }, { + {Access::TransferWrite, Access::ShaderRead, + ImageLayout::Preinitialized, ImageLayout::ShaderReadOnly, + image, ImageAspect::Color} + }) + .end(); + + /* Does not do anything visible, so just test that it didn't blow up */ + CORRADE_VERIFY(true); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::PipelineVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 226c3a362..572d647bb 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -40,6 +40,7 @@ enum class Access: UnsignedInt; typedef Containers::EnumSet Accesses; class Buffer; class BufferCreateInfo; +class BufferMemoryBarrier; class CommandBuffer; /* CommandBufferBeginInfo is useful only in combination with CommandBuffer */ class CommandPool; @@ -66,6 +67,7 @@ typedef Containers::EnumSet ImageAspects; enum class ImageLayout: Int; class ImageCreateInfo; /* Not forward-declaring ImageCreateInfo1D etc right now, I see no need */ +class ImageMemoryBarrier; class ImageView; class ImageViewCreateInfo; /* Not forward-declaring ImageViewCreateInfo1D etc right now, I see no need */ @@ -76,6 +78,7 @@ class InstanceExtensionProperties; class LayerProperties; class Memory; class MemoryAllocateInfo; +class MemoryBarrier; class MemoryMapDeleter; class MemoryRequirements; enum class MemoryFlag: UnsignedInt;