diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index f4bd98f79..4a5cd8fdd 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -51,7 +51,7 @@ Legend: Vulkan function | Matching API --------------------------------------- | ------------ -@fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | | +@fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | @ref CommandPool::allocate(), @ref CommandBuffer destructor @fn_vk{AllocateDescriptorSets}, \n @fn_vk{FreeDescriptorSets} | | @fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | | @@ -310,6 +310,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ +@type_vk{CommandBufferAllocateInfo} | not exposed, internal to @ref CommandPool::allocate() @type_vk{CommandPoolCreateInfo} | @ref CommandPoolCreateInfo @subsection vulkan-mapping-structures-d D diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 0504291d0..8b5f87d8e 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + CommandBuffer.cpp CommandPool.cpp Extensions.cpp Handle.cpp @@ -46,6 +47,7 @@ set(MagnumVk_GracefulAssert_SRCS LayerProperties.cpp) set(MagnumVk_HEADERS + CommandBuffer.h CommandPool.h Device.h DeviceProperties.h diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp new file mode 100644 index 000000000..0e4244c67 --- /dev/null +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -0,0 +1,68 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "CommandBuffer.h" + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" + +namespace Magnum { namespace Vk { + +CommandBuffer CommandBuffer::wrap(Device& device, const VkCommandPool pool, const VkCommandBuffer handle, const HandleFlags flags) { + CommandBuffer out{NoCreate}; + out._device = &device; + out._pool = pool; + out._handle = handle; + out._flags = flags; + return out; +} + +CommandBuffer::CommandBuffer(NoCreateT) noexcept: _device{}, _pool{}, _handle{} {} + +CommandBuffer::CommandBuffer(CommandBuffer&& other) noexcept: _device{other._device}, _pool{other._pool}, _handle{other._handle}, _flags{other._flags} { + other._handle = nullptr; +} + +CommandBuffer::~CommandBuffer() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).FreeCommandBuffers(*_device, _pool, 1, &_handle); +} + +CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._pool, _pool); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkCommandBuffer CommandBuffer::release() { + const VkCommandBuffer handle = _handle; + _handle = nullptr; + return handle; +} + +}} diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h new file mode 100644 index 000000000..adc7c3153 --- /dev/null +++ b/src/Magnum/Vk/CommandBuffer.h @@ -0,0 +1,129 @@ +#ifndef Magnum_Vk_CommandBuffer_h +#define Magnum_Vk_CommandBuffer_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::CommandBuffer + * @m_since_latest + */ + +#include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Magnum.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Command buffer +@m_since_latest + +Wraps a @type_vk_keyword{CommandBuffer}. +*/ +class MAGNUM_VK_EXPORT CommandBuffer { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the command buffer is created on + * @param pool Command pool the buffer is allocated from + * @param handle The @type_vk{CommandBuffer} handle + * @param flags Handle flags + * + * The @p handle is expected to be of an existing Vulkan command + * buffer. Unlike a command buffer allocated using + * @ref CommandPool::allocate(), the Vulkan command buffer is by + * default not deleted on destruction, use @p flags for different + * behavior. + */ + static CommandBuffer wrap(Device& device, VkCommandPool pool, VkCommandBuffer handle, HandleFlags flags = {}); + + /** + * @brief Construct without creating the instance + * + * 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 CommandBuffer(NoCreateT) noexcept; + + /** @brief Copying is not allowed */ + CommandBuffer(const CommandBuffer&) = delete; + + /** @brief Move constructor */ + CommandBuffer(CommandBuffer&& other) noexcept; + + /** + * @brief Destructor + * + * Frees associated @type_vk{CommandBuffer} handle, unless the + * instance was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{FreeCommandBuffers}, @ref release() + */ + ~CommandBuffer(); + + /** @brief Copying is not allowed */ + CommandBuffer& operator=(const CommandBuffer&) = delete; + + /** @brief Move assignment */ + CommandBuffer& operator=(CommandBuffer&& other) noexcept; + + /** @brief Underlying @type_vk{CommandBuffer} handle */ + VkCommandBuffer handle() { return _handle; } + /** @overload */ + operator VkCommandBuffer() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan command buffer + * + * Releases ownership of the Vulkan command buffer and returns its + * handle so @fn_vk{FreeCommandBuffers} is not called on destruction. + * The internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkCommandBuffer release(); + + private: + friend CommandPool; + + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkCommandPool _pool; /* Used only for vkFreeCommandBuffers() */ + VkCommandBuffer _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp index 35e906496..c921bed95 100644 --- a/src/Magnum/Vk/CommandPool.cpp +++ b/src/Magnum/Vk/CommandPool.cpp @@ -25,6 +25,7 @@ #include "CommandPool.h" +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Result.h" @@ -75,6 +76,22 @@ CommandPool& CommandPool::operator=(CommandPool&& other) noexcept { return *this; } +CommandBuffer CommandPool::allocate(const CommandBufferLevel level) { + CommandBuffer out{NoCreate}; + out._device = _device; + out._pool = _handle; + out._flags = HandleFlag::DestroyOnDestruction; + + VkCommandBufferAllocateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = _handle; + info.commandBufferCount = 1; + info.level = VkCommandBufferLevel(level); + MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).AllocateCommandBuffers(*_device, &info, &out._handle)); + + return out; +} + VkCommandPool CommandPool::release() { const VkCommandPool handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h index 02d01117f..7abacbe77 100644 --- a/src/Magnum/Vk/CommandPool.h +++ b/src/Magnum/Vk/CommandPool.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool + * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool, enum @ref Magnum::Vk::CommandBufferLevel * @m_since_latest */ @@ -130,6 +130,22 @@ class MAGNUM_VK_EXPORT CommandPoolCreateInfo { CORRADE_ENUMSET_OPERATORS(CommandPoolCreateInfo::Flags) +/** +@brief Command buffer level +@m_since_latest + +Wraps a @type_vk_keyword{CommandBufferLevel}. +@m_enum_values_as_keywords +@see @ref CommandPool::allocate() +*/ +enum class CommandBufferLevel: Int { + /** Primary command buffer */ + Primary = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + + /** Secondary command buffer */ + Secondary = VK_COMMAND_BUFFER_LEVEL_SECONDARY +}; + /** @brief Command pool @m_since_latest @@ -199,6 +215,13 @@ class MAGNUM_VK_EXPORT CommandPool { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Allocate a command buffer + * + * @see @fn_vk_keyword{AllocateCommandBuffers} + */ + CommandBuffer allocate(CommandBufferLevel level = CommandBufferLevel::Primary); + /** * @brief Release the underlying Vulkan command pool * diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index c7e59593e..f65e42b55 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkCommandBufferTest CommandBufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) @@ -38,6 +39,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) diff --git a/src/Magnum/Vk/Test/CommandBufferTest.cpp b/src/Magnum/Vk/Test/CommandBufferTest.cpp new file mode 100644 index 000000000..87fd6f2d6 --- /dev/null +++ b/src/Magnum/Vk/Test/CommandBufferTest.cpp @@ -0,0 +1,62 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Vk/CommandBuffer.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandBufferTest: TestSuite::Tester { + explicit CommandBufferTest(); + + void constructNoCreate(); + void constructCopy(); +}; + +CommandBufferTest::CommandBufferTest() { + addTests({&CommandBufferTest::constructNoCreate, + &CommandBufferTest::constructCopy}); +} + +void CommandBufferTest::constructNoCreate() { + { + CommandBuffer buffer{NoCreate}; + CORRADE_VERIFY(!buffer.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void CommandBufferTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandBufferTest) diff --git a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp new file mode 100644 index 000000000..fb637ca4a --- /dev/null +++ b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPool.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandBufferVkTest: VulkanTester { + explicit CommandBufferVkTest(); + + void construct(); + void constructMove(); + void wrap(); +}; + +CommandBufferVkTest::CommandBufferVkTest() { + addTests({&CommandBufferVkTest::construct, + &CommandBufferVkTest::constructMove, + &CommandBufferVkTest::wrap}); +} + +void CommandBufferVkTest::construct() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + { + CommandBuffer buffer = pool.allocate(); + CORRADE_VERIFY(buffer.handle()); + CORRADE_COMPARE(buffer.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void CommandBufferVkTest::constructMove() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + CommandBuffer a = pool.allocate(); + VkCommandBuffer handle = a.handle(); + + CommandBuffer b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + CommandBuffer 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 CommandBufferVkTest::wrap() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + VkCommandBuffer buffer{}; + VkCommandBufferAllocateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = pool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + CORRADE_COMPARE(Result(device()->AllocateCommandBuffers(device(), &info, &buffer)), Result::Success); + CORRADE_VERIFY(buffer); + + auto wrapped = CommandBuffer::wrap(device(), pool, buffer, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), buffer); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), buffer); + CORRADE_VERIFY(!wrapped.handle()); + device()->FreeCommandBuffers(device(), pool, 1, &buffer); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandBufferVkTest) diff --git a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp index 4846bf6f8..3278e4d07 100644 --- a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp @@ -23,6 +23,7 @@ DEALINGS IN THE SOFTWARE. */ +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" @@ -37,12 +38,16 @@ struct CommandPoolVkTest: VulkanTester { void construct(); void constructMove(); void wrap(); + + void allocate(); }; CommandPoolVkTest::CommandPoolVkTest() { addTests({&CommandPoolVkTest::construct, &CommandPoolVkTest::constructMove, - &CommandPoolVkTest::wrap}); + &CommandPoolVkTest::wrap, + + &CommandPoolVkTest::allocate}); } void CommandPoolVkTest::construct() { @@ -97,6 +102,14 @@ void CommandPoolVkTest::wrap() { device()->DestroyCommandPool(device(), pool, nullptr); } +void CommandPoolVkTest::allocate() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + CommandBuffer a = pool.allocate(CommandBufferLevel::Secondary); + CORRADE_VERIFY(a.handle()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandPoolVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 171a2a170..57138970e 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class CommandBuffer; class CommandPool; class Device; class DeviceCreateInfo;