diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index aa91d1dde..439cb6ccb 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -53,7 +53,7 @@ Vulkan function | Matching API --------------------------------------- | ------------ @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} | | +@fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | @ref Memory constructor and destructor @subsection vulkan-mapping-functions-b B @@ -332,6 +332,14 @@ Vulkan structure | Matching API @type_vk{ImageMemoryRequirementsInfo} | not exposed, internal to @ref Image::memoryRequirements() @type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo +@subsection vulkan-mapping-structures-m M + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{MemoryAllocateInfo} | @ref MemoryAllocateInfo + */ }} diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 44b1a1be6..0eb299b4d 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -28,6 +28,10 @@ #include #include +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" + namespace Magnum { namespace Vk { MemoryRequirements::MemoryRequirements(): _requirements{} { @@ -41,6 +45,56 @@ MemoryRequirements::MemoryRequirements(const VkMemoryRequirements2& requirements member instead of doing a copy */ _requirements(requirements) {} +MemoryAllocateInfo::MemoryAllocateInfo(UnsignedLong size, UnsignedInt memory): _info{} { + _info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + _info.allocationSize = size; + _info.memoryTypeIndex = memory; +} + +MemoryAllocateInfo::MemoryAllocateInfo(NoInitT) noexcept {} + +MemoryAllocateInfo::MemoryAllocateInfo(const VkMemoryAllocateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Memory Memory::wrap(Device& device, const VkDeviceMemory handle, const HandleFlags flags) { + Memory out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Memory::Memory(Device& device, const MemoryAllocateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->AllocateMemory(device, info, nullptr, &_handle)); +} + +Memory::Memory(NoCreateT): _device{}, _handle{} {} + +Memory::Memory(Memory&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Memory::~Memory() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).FreeMemory(*_device, _handle, nullptr); +} + +Memory& Memory::operator=(Memory&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkDeviceMemory Memory::release() { + const VkDeviceMemory handle = _handle; + _handle = {}; + return handle; +} + Debug& operator<<(Debug& debug, const MemoryFlag value) { debug << "Vk::MemoryFlag" << Debug::nospace; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 12f56ef33..6092be9e4 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::MemoryRequirements, enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags + * @brief Class @ref Magnum::Vk::MemoryRequirements, @ref Magnum::Vk::MemoryAllocateInfo, @ref Magnum::Vk::Memory, enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags */ #include @@ -174,6 +174,153 @@ class MAGNUM_VK_EXPORT MemoryRequirements { VkMemoryRequirements2 _requirements; }; +/** +@brief Memory allocation info +@m_since_latest + +Wraps a @type_vk_keyword{MemoryAllocateInfo}. See @ref Memory for usage +information. +*/ +class MAGNUM_VK_EXPORT MemoryAllocateInfo { + public: + /** @todo Flags, in VkMemoryAllocateFlagsInfo (1.1) */ + + /** + * @brief Constructor + * @param size Allocation size in bytes + * @param memory Memory index, smaller than + * @ref DeviceProperties::memoryCount() + * + * The following @type_vk{MemoryAllocateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `allocationSize` to @p size + * - `memoryTypeIndex` to @p memory + * + * @see @ref DeviceProperties::pickMemory() + */ + explicit MemoryAllocateInfo(UnsignedLong size, UnsignedInt memory); + + /** + * @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 MemoryAllocateInfo(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 MemoryAllocateInfo(const VkMemoryAllocateInfo& info); + + /** @brief Underlying @type_vk{MemoryAllocateInfo} structure */ + VkMemoryAllocateInfo& operator*() { return _info; } + /** @overload */ + const VkMemoryAllocateInfo& operator*() const { return _info; } + /** @overload */ + VkMemoryAllocateInfo* operator->() { return &_info; } + /** @overload */ + const VkMemoryAllocateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkMemoryAllocateInfo*() const { return &_info; } + + private: + VkMemoryAllocateInfo _info; +}; + +/** +@brief Device memory +@m_since_latest + +Wraps a @type_vk_keyword{DeviceMemory}. +*/ +class MAGNUM_VK_EXPORT Memory { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the memory is allocated on + * @param handle The @type_vk{DeviceMemory} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a memory allocated using a constructor, the Vulkan memory is by + * default not freed on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Memory wrap(Device& device, VkDeviceMemory handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to allocate the memory on + * @param info Memory allocation info + * + * @see @fn_vk_keyword{AllocateMemory} + */ + explicit Memory(Device& device, const MemoryAllocateInfo& info); + + /** + * @brief Construct without allocating the memory + * + * 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 Memory(NoCreateT); + + /** @brief Copying is not allowed */ + Memory(const Memory&) = delete; + + /** @brief Move constructor */ + Memory(Memory&& other) noexcept; + + /** + * @brief Destructor + * + * Frees associated @type_vk{DeviceMemory} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{FreeMemory}, @ref release() + */ + ~Memory(); + + /** @brief Copying is not allowed */ + Memory& operator=(const Memory&) = delete; + + /** @brief Move assignment */ + Memory& operator=(Memory&& other) noexcept; + + /** @brief Underlying @type_vk{DeviceMemory} handle */ + VkDeviceMemory handle() { return _handle; } + /** @overload */ + operator VkDeviceMemory() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan memory + * + * Releases ownership of the Vulkan memory and returns its handle so + * @fn_vk{FreeMemory} is not called on destruction. The internal state + * is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkDeviceMemory release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkDeviceMemory _handle; + HandleFlags _flags; +}; + }} #endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 40d6f89c2..9f52fd331 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -49,5 +49,6 @@ if(BUILD_VK_TESTS) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) + corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/MemoryTest.cpp b/src/Magnum/Vk/Test/MemoryTest.cpp index d19e1052b..010df0b1e 100644 --- a/src/Magnum/Vk/Test/MemoryTest.cpp +++ b/src/Magnum/Vk/Test/MemoryTest.cpp @@ -37,6 +37,13 @@ struct MemoryTest: TestSuite::Tester { void requirementsConstructNoInit(); void requirementsConstructFromVk(); + void allocateInfoConstruct(); + void allocateInfoConstructNoInit(); + void allocateInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); + void debugMemoryFlag(); void debugMemoryFlags(); }; @@ -45,6 +52,13 @@ MemoryTest::MemoryTest() { addTests({&MemoryTest::requirementsConstructNoInit, &MemoryTest::requirementsConstructFromVk, + &MemoryTest::allocateInfoConstruct, + &MemoryTest::allocateInfoConstructNoInit, + &MemoryTest::allocateInfoConstructFromVk, + + &MemoryTest::constructNoCreate, + &MemoryTest::constructCopy, + &MemoryTest::debugMemoryFlag, &MemoryTest::debugMemoryFlags}); } @@ -69,6 +83,47 @@ void MemoryTest::requirementsConstructFromVk() { CORRADE_COMPARE(requirements->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); } +void MemoryTest::allocateInfoConstruct() { + MemoryAllocateInfo info{65536, 1}; + CORRADE_COMPARE(info->allocationSize, 65536); + CORRADE_COMPARE(info->memoryTypeIndex, 1); +} + +void MemoryTest::allocateInfoConstructNoInit() { + MemoryAllocateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) MemoryAllocateInfo{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 MemoryTest::allocateInfoConstructFromVk() { + VkMemoryAllocateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + MemoryAllocateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void MemoryTest::constructNoCreate() { + { + Memory memory{NoCreate}; + CORRADE_VERIFY(!memory.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void MemoryTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + void MemoryTest::debugMemoryFlag() { std::ostringstream out; Debug{&out} << MemoryFlag::HostCached << MemoryFlag(0xdeadcafe); diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp new file mode 100644 index 000000000..16ac06f12 --- /dev/null +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -0,0 +1,94 @@ +/* + 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/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Memory.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct MemoryVkTest: VulkanTester { + explicit MemoryVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +MemoryVkTest::MemoryVkTest() { + addTests({&MemoryVkTest::construct, + &MemoryVkTest::constructMove, + + &MemoryVkTest::wrap}); +} + +void MemoryVkTest::construct() { + Memory memory{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + CORRADE_VERIFY(memory.handle()); + CORRADE_COMPARE(memory.handleFlags(), HandleFlag::DestroyOnDestruction); +} + +void MemoryVkTest::constructMove() { + Memory a{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + VkDeviceMemory handle = a.handle(); + + Memory b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Memory 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 MemoryVkTest::wrap() { + VkDeviceMemory memory{}; + CORRADE_COMPARE(Result(device()->AllocateMemory(device(), + MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}, + nullptr, &memory)), Result::Success); + CORRADE_VERIFY(memory); + + auto wrapped = Memory::wrap(device(), memory, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), memory); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), memory); + CORRADE_VERIFY(!wrapped.handle()); + device()->FreeMemory(device(), memory, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index f942fc453..025ac28fc 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -52,6 +52,7 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +class Memory; class MemoryRequirements; enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags;