diff --git a/include/merian/vk/memory/memory_allocator.hpp b/include/merian/vk/memory/memory_allocator.hpp index a1c3b29..f3b9366 100644 --- a/include/merian/vk/memory/memory_allocator.hpp +++ b/include/merian/vk/memory/memory_allocator.hpp @@ -116,15 +116,10 @@ inline std::string format_as(const MemoryAllocationInfo& alloc_info) { */ class MemoryAllocation : public std::enable_shared_from_this { public: - MemoryAllocation(const ContextHandle& context) : context(context) {} + MemoryAllocation(const ContextHandle& context); // unmaps and frees the memory when called - virtual ~MemoryAllocation() = default; - - // ------------------------------------------------------------------------------------ - - // can only be called once - virtual void free() = 0; + virtual ~MemoryAllocation(); // ------------------------------------------------------------------------------------ @@ -133,7 +128,19 @@ class MemoryAllocation : public std::enable_shared_from_this { return (T*)map(); } - // Maps device memory to system memory. + // Invalidates memory of a allocation. Call this before reading from non host-coherent memory + // type or before reading from persistently mapped host-coherent memory. + // Map does not do that automatically, internally this is a call to + // vkInvalidateMappedMemoryRanges + virtual void invalidate(const VkDeviceSize offset = 0, const VkDeviceSize size = VK_WHOLE_SIZE) = 0; + + // Call this after writing to non host-coherent memory + // or after writing to persistently mapped host-coherent memory. + // Map does not do that automatically, internally this is a call to vkFlushMappedMemoryRanges + virtual void flush(const VkDeviceSize offset = 0, const VkDeviceSize size = VK_WHOLE_SIZE) = 0; + + // Maps device memory to system memory. This should return the same pointer if called multiple + // times before "unmap". virtual void* map() = 0; // Unmap memHandle diff --git a/include/merian/vk/memory/memory_allocator_vma.hpp b/include/merian/vk/memory/memory_allocator_vma.hpp index 7c94edc..999d4d2 100644 --- a/include/merian/vk/memory/memory_allocator_vma.hpp +++ b/include/merian/vk/memory/memory_allocator_vma.hpp @@ -35,14 +35,12 @@ class VMAMemoryAllocation : public MemoryAllocation { // ------------------------------------------------------------------------------------ - void free() override; + void invalidate(const VkDeviceSize offset = 0, const VkDeviceSize size = VK_WHOLE_SIZE) override; - // ------------------------------------------------------------------------------------ + void flush(const VkDeviceSize offset = 0, const VkDeviceSize size = VK_WHOLE_SIZE) override; - // Maps device memory to system memory. void* map() override; - // Unmap memHandle void unmap() override; // ------------------------------------------------------------------------------------ @@ -52,7 +50,7 @@ class VMAMemoryAllocation : public MemoryAllocation { MemoryAllocationInfo get_memory_info() const override; // ------------------------------------------------------------------------------------ - + ImageHandle create_aliasing_image(const vk::ImageCreateInfo& image_create_info) override; BufferHandle create_aliasing_buffer(const vk::BufferCreateInfo& buffer_create_info) override; @@ -68,11 +66,13 @@ class VMAMemoryAllocation : public MemoryAllocation { void properties(Properties& props) override; private: + void free(); + const std::shared_ptr allocator; const MemoryMappingType mapping_type; VmaAllocation m_allocation; mutable std::mutex allocation_mutex; - bool is_mapped = false; + void* mapped_memory = nullptr; }; class VMAMemoryAllocator : public MemoryAllocator { @@ -95,13 +95,14 @@ class VMAMemoryAllocator : public MemoryAllocator { // ------------------------------------------------------------------------------------ - MemoryAllocationHandle allocate_memory(const vk::MemoryPropertyFlags required_flags, - const vk::MemoryRequirements& requirements, - const std::string& debug_name = {}, - const MemoryMappingType mapping_type = MemoryMappingType::NONE, - const vk::MemoryPropertyFlags preferred_flags = {}, - const bool dedicated = false, - const float dedicated_priority = 1.0) override; + MemoryAllocationHandle + allocate_memory(const vk::MemoryPropertyFlags required_flags, + const vk::MemoryRequirements& requirements, + const std::string& debug_name = {}, + const MemoryMappingType mapping_type = MemoryMappingType::NONE, + const vk::MemoryPropertyFlags preferred_flags = {}, + const bool dedicated = false, + const float dedicated_priority = 1.0) override; BufferHandle create_buffer(const vk::BufferCreateInfo buffer_create_info, diff --git a/src/merian/vk/memory/memory_allocator.cpp b/src/merian/vk/memory/memory_allocator.cpp index f380282..4e0678c 100644 --- a/src/merian/vk/memory/memory_allocator.cpp +++ b/src/merian/vk/memory/memory_allocator.cpp @@ -15,4 +15,9 @@ uint32_t getMemoryType(const vk::PhysicalDeviceMemoryProperties& memoryPropertie return ~0u; } +MemoryAllocation::MemoryAllocation(const ContextHandle& context) : context(context) {} + +// unmaps and frees the memory when called +MemoryAllocation::~MemoryAllocation() {} + } // namespace merian diff --git a/src/merian/vk/memory/memory_allocator_vma.cpp b/src/merian/vk/memory/memory_allocator_vma.cpp index a0574d9..ec8271b 100644 --- a/src/merian/vk/memory/memory_allocator_vma.cpp +++ b/src/merian/vk/memory/memory_allocator_vma.cpp @@ -9,9 +9,7 @@ namespace merian { VMAMemoryAllocation::~VMAMemoryAllocation() { SPDLOG_TRACE("destroy VMA allocation ({})", fmt::ptr(this)); - if (is_mapped) { - unmap(); - } + unmap(); free(); }; @@ -19,38 +17,50 @@ VMAMemoryAllocation::~VMAMemoryAllocation() { void VMAMemoryAllocation::free() { std::lock_guard lock(allocation_mutex); + assert(m_allocation); - if (m_allocation) { - SPDLOG_TRACE("freeing memory ({})", fmt::ptr(this)); - vmaFreeMemory(allocator->vma_allocator, m_allocation); - m_allocation = nullptr; - } else { - SPDLOG_WARN("VMA allocation ({}) was already freed", fmt::ptr(this)); - } + SPDLOG_TRACE("freeing memory ({})", fmt::ptr(this)); + vmaFreeMemory(allocator->vma_allocator, m_allocation); + m_allocation = nullptr; }; // ------------------------------------------------------------------------------------ +void VMAMemoryAllocation::invalidate(const VkDeviceSize offset, const VkDeviceSize size) { + vmaInvalidateAllocation(allocator->vma_allocator, m_allocation, offset, size); +}; + +void VMAMemoryAllocation::flush(const VkDeviceSize offset, const VkDeviceSize size) { + vmaFlushAllocation(allocator->vma_allocator, m_allocation, offset, size); +}; + // Maps device memory to system memory. void* VMAMemoryAllocation::map() { std::lock_guard lock(allocation_mutex); + assert(m_allocation); // freed? assert(mapping_type != MemoryMappingType::NONE); - void* ptr; - check_result(vmaMapMemory(allocator->vma_allocator, m_allocation, &ptr), + if (mapped_memory != nullptr) { + return mapped_memory; + } + + check_result(vmaMapMemory(allocator->vma_allocator, m_allocation, &mapped_memory), "mapping memory failed"); - is_mapped = true; - return ptr; + return mapped_memory; }; // Unmap memHandle void VMAMemoryAllocation::unmap() { std::lock_guard lock(allocation_mutex); - assert(m_allocation); // freed? + assert(m_allocation); + + if (mapped_memory == nullptr) { + return; + } vmaUnmapMemory(allocator->vma_allocator, m_allocation); - is_mapped = false; + mapped_memory = nullptr; }; // ------------------------------------------------------------------------------------ @@ -87,10 +97,10 @@ VMAMemoryAllocation::create_aliasing_buffer(const vk::BufferCreateInfo& buffer_c MemoryAllocationInfo VMAMemoryAllocation::get_memory_info() const { const std::lock_guard lock(allocation_mutex); - VmaAllocationInfo allocInfo; - vmaGetAllocationInfo(allocator->vma_allocator, m_allocation, &allocInfo); - return MemoryAllocationInfo{allocInfo.deviceMemory, allocInfo.offset, allocInfo.size, - allocInfo.pName}; + VmaAllocationInfo alloc_info; + vmaGetAllocationInfo(allocator->vma_allocator, m_allocation, &alloc_info); + return MemoryAllocationInfo{alloc_info.deviceMemory, alloc_info.offset, alloc_info.size, + alloc_info.pName}; }; MemoryAllocatorHandle VMAMemoryAllocation::get_allocator() const { @@ -99,7 +109,11 @@ MemoryAllocatorHandle VMAMemoryAllocation::get_allocator() const { void VMAMemoryAllocation::properties(Properties& props) { MemoryAllocation::properties(props); - props.output_text(fmt::format("Mapped: {}", is_mapped)); + + props.output_text(fmt::format("Mapped: {}", mapped_memory != nullptr)); + if (mapped_memory != nullptr) { + props.output_text(fmt::format("Mapped at: {}", fmt::ptr(mapped_memory))); + } } //-------------------------------------------------------------------------------------------------- @@ -124,23 +138,23 @@ VMAMemoryAllocator::make_allocator(const ContextHandle& context, VMAMemoryAllocator::VMAMemoryAllocator(const ContextHandle& context, const VmaAllocatorCreateFlags flags) : MemoryAllocator(context) { - VmaAllocatorCreateInfo allocatorInfo = {.flags = flags, - .physicalDevice = - context->physical_device.physical_device, - .device = context->device, - .preferredLargeHeapBlockSize = 0, - .pAllocationCallbacks = nullptr, - .pDeviceMemoryCallbacks = nullptr, - .pHeapSizeLimit = nullptr, - .pVulkanFunctions = nullptr, - .instance = context->instance, - .vulkanApiVersion = context->vk_api_version, + VmaAllocatorCreateInfo allocator_info = {.flags = flags, + .physicalDevice = + context->physical_device.physical_device, + .device = context->device, + .preferredLargeHeapBlockSize = 0, + .pAllocationCallbacks = nullptr, + .pDeviceMemoryCallbacks = nullptr, + .pHeapSizeLimit = nullptr, + .pVulkanFunctions = nullptr, + .instance = context->instance, + .vulkanApiVersion = context->vk_api_version, #if VMA_EXTERNAL_MEMORY - .pTypeExternalMemoryHandleTypes = nullptr + .pTypeExternalMemoryHandleTypes = nullptr #endif }; SPDLOG_DEBUG("create VMA allocator ({})", fmt::ptr(this)); - vmaCreateAllocator(&allocatorInfo, &vma_allocator); + vmaCreateAllocator(&allocator_info, &vma_allocator); } VMAMemoryAllocator::~VMAMemoryAllocator() { @@ -151,7 +165,7 @@ VMAMemoryAllocator::~VMAMemoryAllocator() { // ---------------------------------------------------------------------------------------------- void log_allocation([[maybe_unused]] const VmaAllocationInfo& info, - [[maybe_unused]] const MemoryAllocationHandle memory, + [[maybe_unused]] const MemoryAllocationHandle& memory, [[maybe_unused]] const std::string& name) { if (!name.empty()) SPDLOG_TRACE("allocated {} of memory at offset {} ({}, {})", format_size(info.size), @@ -176,7 +190,7 @@ VmaAllocationCreateInfo make_create_info(const VmaMemoryUsage usage, const MemoryMappingType mapping_type, const bool dedicated, const float dedicated_priority) { - VmaAllocationCreateInfo vmaAllocInfo{ + VmaAllocationCreateInfo vma_alloc_info{ .flags = {}, .usage = usage, .requiredFlags = static_cast(required_flags), @@ -187,11 +201,11 @@ VmaAllocationCreateInfo make_create_info(const VmaMemoryUsage usage, .priority = dedicated_priority, }; // clang-format off - vmaAllocInfo.flags |= dedicated ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0; - vmaAllocInfo.flags |= mapping_type == MemoryMappingType::HOST_ACCESS_RANDOM ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; - vmaAllocInfo.flags |= mapping_type == MemoryMappingType::HOST_ACCESS_SEQUENTIAL_WRITE ? VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT : 0; + vma_alloc_info.flags |= dedicated ? VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT : 0; + vma_alloc_info.flags |= mapping_type == MemoryMappingType::HOST_ACCESS_RANDOM ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; + vma_alloc_info.flags |= mapping_type == MemoryMappingType::HOST_ACCESS_SEQUENTIAL_WRITE ? VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT : 0; // clang-format on - return vmaAllocInfo; + return vma_alloc_info; } MemoryAllocationHandle @@ -202,7 +216,7 @@ VMAMemoryAllocator::allocate_memory(const vk::MemoryPropertyFlags required_flags const vk::MemoryPropertyFlags preferred_flags, const bool dedicated, const float dedicated_priority) { - VmaAllocationCreateInfo vmaAllocInfo = + VmaAllocationCreateInfo vma_alloc_info = make_create_info(VMA_MEMORY_USAGE_UNKNOWN, required_flags, preferred_flags, mapping_type, dedicated, dedicated_priority); @@ -211,7 +225,7 @@ VMAMemoryAllocator::allocate_memory(const vk::MemoryPropertyFlags required_flags VmaAllocationInfo allocation_info; VmaAllocation allocation; check_result( - vmaAllocateMemory(vma_allocator, &mem_reqs, &vmaAllocInfo, &allocation, &allocation_info), + vmaAllocateMemory(vma_allocator, &mem_reqs, &vma_alloc_info, &allocation, &allocation_info), "could not allocate memory"); if (!debug_name.empty())