Browse Source

Vk: implement memory mapping.

pull/234/head
Vladimír Vondruš 6 years ago
parent
commit
7e5d811450
  1. 25
      src/Magnum/Vk/Memory.cpp
  2. 64
      src/Magnum/Vk/Memory.h
  3. 45
      src/Magnum/Vk/Test/MemoryVkTest.cpp
  4. 1
      src/Magnum/Vk/Vk.h

25
src/Magnum/Vk/Memory.cpp

@ -25,6 +25,7 @@
#include "Memory.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Utility/Debug.h>
@ -92,6 +93,30 @@ Memory& Memory::operator=(Memory&& other) noexcept {
return *this;
}
Containers::Array<char, MemoryMapDeleter> 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<char, MemoryMapDeleter>{static_cast<char*>(data), size, MemoryMapDeleter{(**_device).UnmapMemory, *_device, _handle}};
}
Containers::Array<char, MemoryMapDeleter> Memory::map() {
return map(0, _size);
}
Containers::Array<const char, MemoryMapDeleter> Memory::mapRead(const UnsignedLong offset, const UnsignedLong size) {
Containers::Array<char, MemoryMapDeleter> 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<const char, MemoryMapDeleter>{out.release(), size, deleter};
}
Containers::Array<const char, MemoryMapDeleter> Memory::mapRead() {
return mapRead(0, _size);
}
VkDeviceMemory Memory::release() {
const VkDeviceMemory handle = _handle;
_handle = {};

64
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<char, MemoryMapDeleter> 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<char, MemoryMapDeleter> 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<const char, MemoryMapDeleter> 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<const char, MemoryMapDeleter> 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

45
src/Magnum/Vk/Test/MemoryVkTest.cpp

@ -23,6 +23,8 @@
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/Containers/Array.h>
#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<char, MemoryMapDeleter> 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<char, MemoryMapDeleter> 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<const char, MemoryMapDeleter> mapped = a.mapRead();
CORRADE_COMPARE(mapped.size(), 1024*1024);
}
/* Map a subrange again */
{
Containers::Array<const char, MemoryMapDeleter> mapped = a.mapRead(1024, 100);
CORRADE_COMPARE(mapped.size(), 100);
}
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryVkTest)

1
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<MemoryFlag> MemoryFlags;

Loading…
Cancel
Save