From 7e5d8114502918343d490630b82cdb308f0e6ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 23:46:12 +0100 Subject: [PATCH] Vk: implement memory mapping. --- src/Magnum/Vk/Memory.cpp | 25 +++++++++++ src/Magnum/Vk/Memory.h | 64 +++++++++++++++++++++++++++++ src/Magnum/Vk/Test/MemoryVkTest.cpp | 45 +++++++++++++++++++- src/Magnum/Vk/Vk.h | 1 + 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index bc4dcae74..2d0f32254 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -25,6 +25,7 @@ #include "Memory.h" +#include #include #include @@ -92,6 +93,30 @@ Memory& Memory::operator=(Memory&& other) noexcept { return *this; } +Containers::Array Memory::map(const UnsignedLong offset, const UnsignedLong size) { + void* data; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).MapMemory(*_device, _handle, offset, size, {}, &data)); + return Containers::Array{static_cast(data), size, MemoryMapDeleter{(**_device).UnmapMemory, *_device, _handle}}; +} + +Containers::Array Memory::map() { + return map(0, _size); +} + +Containers::Array Memory::mapRead(const UnsignedLong offset, const UnsignedLong size) { + Containers::Array out = map(offset, size); + + /* Simply "cast" to a const array. Extracting the deleter before because + the order of operations is unspecified and the deleter could be queried + after release() got called */ + const MemoryMapDeleter deleter = out.deleter(); + return Containers::Array{out.release(), size, deleter}; +} + +Containers::Array Memory::mapRead() { + return mapRead(0, _size); +} + VkDeviceMemory Memory::release() { const VkDeviceMemory handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 292754d50..f344435d9 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -39,6 +39,15 @@ namespace Magnum { namespace Vk { +/** @relates Memory +@brief Deleter for mapped memory +@m_since_latest + +Deleter for the array returned from @ref Memory::map(). Calls +@fn_vk_keyword{UnmapMemory}. +*/ +class MemoryMapDeleter; + /** @brief Memory type flag @m_since_latest @@ -307,6 +316,45 @@ class MAGNUM_VK_EXPORT Memory { /** @brief Memory allocation size */ UnsignedLong size() const { return _size; } + /** + * @brief Map a memory range + * @param offset Byte offset + * @param size Memory size + * + * The returned array size is @p size and the deleter performs an + * unmap. For this operation to work, the memory has to be allocated + * with @ref MemoryFlag::HostVisible and the @p offset and @p size be + * in bounds for @ref size(). + * @see @fn_vk_keyword{MapMemory}, @fn_vk{UnmapMemory} + */ + Containers::Array map(UnsignedLong offset, UnsignedLong size); + + /** + * @brief Map the whole memory + * + * Equivalent to calling @ref map(UnsignedLong, UnsignedLong) with + * @cpp 0 @ce and @ref size(). + */ + Containers::Array map(); + + /** + * @brief Map a memory range read-only + * + * Like @ref map(UnsignedLong, UnsignedLong) but returning a + * @cpp const @ce array. Currently Vulkan doesn't have any flags to + * control read/write access, so apart from a different return type the + * behavior is equivalent. + */ + Containers::Array mapRead(UnsignedLong offset, UnsignedLong size); + + /** + * @brief Map the whole memory read-only + * + * Equivalent to calling @ref mapRead(UnsignedLong, UnsignedLong) with + * @cpp 0 @ce and @ref size(). + */ + Containers::Array mapRead(); + /** * @brief Release the underlying Vulkan memory * @@ -326,6 +374,22 @@ class MAGNUM_VK_EXPORT Memory { UnsignedLong _size; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +class MAGNUM_VK_EXPORT MemoryMapDeleter { + public: + explicit MemoryMapDeleter(): _unmap{}, _device{}, _memory{} {} + explicit MemoryMapDeleter(void(*unmap)(VkDevice, VkDeviceMemory), VkDevice device, VkDeviceMemory memory): _unmap{unmap}, _device{device}, _memory{memory} {} + void operator()(const char*, std::size_t) { + if(_unmap) _unmap(_device, _memory); + } + + private: + void(*_unmap)(VkDevice, VkDeviceMemory); + VkDevice _device; + VkDeviceMemory _memory; +}; +#endif + }} #endif diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp index 2ed835235..ff820390b 100644 --- a/src/Magnum/Vk/Test/MemoryVkTest.cpp +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -23,6 +23,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Memory.h" @@ -38,13 +40,19 @@ struct MemoryVkTest: VulkanTester { void constructMove(); void wrap(); + + void map(); + void mapRead(); }; MemoryVkTest::MemoryVkTest() { addTests({&MemoryVkTest::construct, &MemoryVkTest::constructMove, - &MemoryVkTest::wrap}); + &MemoryVkTest::wrap, + + &MemoryVkTest::map, + &MemoryVkTest::mapRead}); } void MemoryVkTest::construct() { @@ -94,6 +102,41 @@ void MemoryVkTest::wrap() { device()->FreeMemory(device(), memory, nullptr); } +void MemoryVkTest::map() { + Memory a{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::HostVisible)}}; + + /* Map and write */ + { + Containers::Array mapped = a.map(); + CORRADE_COMPARE(mapped.size(), 1024*1024); + mapped[1024 + 37] = 'c'; + } + + /* Map a subrange again -- shouldn't fail since we unmapped implicitly + above */ + { + Containers::Array mapped = a.map(1024, 100); + CORRADE_COMPARE(mapped.size(), 100); + CORRADE_COMPARE(mapped[37], 'c'); + } +} + +void MemoryVkTest::mapRead() { + Memory a{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::HostVisible)}}; + + /* Map and write, unmap should be implicit */ + { + Containers::Array mapped = a.mapRead(); + CORRADE_COMPARE(mapped.size(), 1024*1024); + } + + /* Map a subrange again */ + { + Containers::Array mapped = a.mapRead(1024, 100); + CORRADE_COMPARE(mapped.size(), 100); + } +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 025ac28fc..cef370bf5 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -53,6 +53,7 @@ class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; class Memory; +class MemoryMapDeleter; class MemoryRequirements; enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags;