Diligent Engine  v.2.4.g
VulkanMemoryManager.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2021 Diligent Graphics LLC
3  * Copyright 2015-2019 Egor Yusov
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * In no event and under no legal theory, whether in tort (including negligence),
18  * contract, or otherwise, unless required by applicable law (such as deliberate
19  * and grossly negligent acts) or agreed to in writing, shall any Contributor be
20  * liable for any damages, including any direct, indirect, special, incidental,
21  * or consequential damages of any character arising as a result of this License or
22  * out of the use or inability to use the software (including but not limited to damages
23  * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
24  * all other commercial damages or losses), even if such Contributor has been advised
25  * of the possibility of such damages.
26  */
27 
28 #pragma once
29 
30 #include <mutex>
31 #include <array>
32 #include <unordered_map>
33 #include <atomic>
34 #include <string>
35 #include "MemoryAllocator.h"
40 #include "HashUtils.hpp"
41 
42 namespace VulkanUtilities
43 {
44 
45 class VulkanMemoryPage;
46 class VulkanMemoryManager;
47 
49 {
50  VulkanMemoryAllocation() noexcept {}
51 
52  // clang-format off
55 
56  VulkanMemoryAllocation(VulkanMemoryPage* _Page, VkDeviceSize _UnalignedOffset, VkDeviceSize _Size)noexcept :
57  Page {_Page },
58  UnalignedOffset{_UnalignedOffset},
59  Size {_Size }
60  {}
61 
63  Page {rhs.Page },
64  UnalignedOffset{rhs.UnalignedOffset},
65  Size {rhs.Size }
66  {
67  rhs.Page = nullptr;
68  rhs.UnalignedOffset = 0;
69  rhs.Size = 0;
70  }
71 
73  {
74  Page = rhs.Page;
75  UnalignedOffset = rhs.UnalignedOffset;
76  Size = rhs.Size;
77 
78  rhs.Page = nullptr;
79  rhs.UnalignedOffset = 0;
80  rhs.Size = 0;
81 
82  return *this;
83  }
84  // clang-format on
85 
86  // Destructor immediately returns the allocation to the parent page.
87  // The allocation must not be in use by the GPU.
89 
90  VulkanMemoryPage* Page = nullptr; // Memory page that contains this allocation
91  VkDeviceSize UnalignedOffset = 0; // Unaligned offset from the start of the memory
92  VkDeviceSize Size = 0; // Reserved size of this allocation
93 };
94 
96 {
97 public:
98  VulkanMemoryPage(VulkanMemoryManager& ParentMemoryMgr,
99  VkDeviceSize PageSize,
100  uint32_t MemoryTypeIndex,
101  bool IsHostVisible,
102  VkMemoryAllocateFlags AllocateFlags) noexcept;
104 
105  // clang-format off
107  m_ParentMemoryMgr {rhs.m_ParentMemoryMgr },
108  m_AllocationMgr {std::move(rhs.m_AllocationMgr)},
109  m_VkMemory {std::move(rhs.m_VkMemory) },
110  m_CPUMemory {rhs.m_CPUMemory }
111  {
112  rhs.m_CPUMemory = nullptr;
113  }
114 
115  VulkanMemoryPage (const VulkanMemoryPage&) = delete;
118 
119  bool IsEmpty() const { return m_AllocationMgr.IsEmpty(); }
120  bool IsFull() const { return m_AllocationMgr.IsFull(); }
121  VkDeviceSize GetPageSize() const { return m_AllocationMgr.GetMaxSize(); }
122  VkDeviceSize GetUsedSize() const { return m_AllocationMgr.GetUsedSize(); }
123 
124  // clang-format on
125 
126  VulkanMemoryAllocation Allocate(VkDeviceSize size, VkDeviceSize alignment);
127 
128  VkDeviceMemory GetVkMemory() const { return m_VkMemory; }
129  void* GetCPUMemory() const { return m_CPUMemory; }
130 
131 private:
132  using AllocationsMgrOffsetType = Diligent::VariableSizeAllocationsManager::OffsetType;
133 
134  friend struct VulkanMemoryAllocation;
135 
136  // Memory is reclaimed immediately. The application is responsible to ensure it is not in use by the GPU
137  void Free(VulkanMemoryAllocation&& Allocation);
138 
139  VulkanMemoryManager& m_ParentMemoryMgr;
140  std::mutex m_Mutex;
143  void* m_CPUMemory = nullptr;
144 };
145 
147 {
148 public:
149  // clang-format off
150  VulkanMemoryManager(std::string MgrName,
151  const VulkanLogicalDevice& LogicalDevice,
152  const VulkanPhysicalDevice& PhysicalDevice,
153  Diligent::IMemoryAllocator& Allocator,
154  VkDeviceSize DeviceLocalPageSize,
155  VkDeviceSize HostVisiblePageSize,
156  VkDeviceSize DeviceLocalReserveSize,
157  VkDeviceSize HostVisibleReserveSize) :
158  m_MgrName {std::move(MgrName) },
159  m_LogicalDevice {LogicalDevice },
160  m_PhysicalDevice {PhysicalDevice },
161  m_Allocator {Allocator },
162  m_DeviceLocalPageSize {DeviceLocalPageSize },
163  m_HostVisiblePageSize {HostVisiblePageSize },
164  m_DeviceLocalReserveSize{DeviceLocalReserveSize},
165  m_HostVisibleReserveSize{HostVisibleReserveSize}
166  {}
167 
168 
169  // We have to write this constructor because on msvc default
170  // constructor is not labeled with noexcept, which makes all
171  // std containers use copy instead of move
173  m_MgrName {std::move(rhs.m_MgrName)},
174  m_LogicalDevice {rhs.m_LogicalDevice },
175  m_PhysicalDevice {rhs.m_PhysicalDevice },
176  m_Allocator {rhs.m_Allocator },
177  m_Pages {std::move(rhs.m_Pages) },
178 
179  m_DeviceLocalPageSize {rhs.m_DeviceLocalPageSize },
180  m_HostVisiblePageSize {rhs.m_HostVisiblePageSize },
181  m_DeviceLocalReserveSize {rhs.m_DeviceLocalReserveSize},
182  m_HostVisibleReserveSize {rhs.m_HostVisibleReserveSize},
183 
184  //m_CurrUsedSize {rhs.m_CurrUsedSize},
185  m_PeakUsedSize {rhs.m_PeakUsedSize },
186  m_CurrAllocatedSize {rhs.m_CurrAllocatedSize},
187  m_PeakAllocatedSize {rhs.m_PeakAllocatedSize}
188  {
189  // clang-format on
190  for (size_t i = 0; i < m_CurrUsedSize.size(); ++i)
191  m_CurrUsedSize[i].store(rhs.m_CurrUsedSize[i].load());
192  }
193 
195 
196  // clang-format off
197  VulkanMemoryManager (const VulkanMemoryManager&) = delete;
200  // clang-format on
201 
202  VulkanMemoryAllocation Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible, VkMemoryAllocateFlags AllocateFlags);
203  VulkanMemoryAllocation Allocate(const VkMemoryRequirements& MemReqs, VkMemoryPropertyFlags MemoryProps, VkMemoryAllocateFlags AllocateFlags);
204  void ShrinkMemory();
205 
206 protected:
207  friend class VulkanMemoryPage;
208 
209  virtual void OnNewPageCreated(VulkanMemoryPage& NewPage) {}
210  virtual void OnPageDestroy(VulkanMemoryPage& Page) {}
211 
212  std::string m_MgrName;
213 
216 
218 
219  std::mutex m_PagesMtx;
221  {
222  const uint32_t MemoryTypeIndex;
223  const VkMemoryAllocateFlags AllocateFlags;
224  const bool IsHostVisible;
225 
226  // clang-format off
227  MemoryPageIndex(uint32_t _MemoryTypeIndex,
228  bool _IsHostVisible,
229  VkMemoryAllocateFlags _AllocateFlags) :
230  MemoryTypeIndex{_MemoryTypeIndex},
231  AllocateFlags {_AllocateFlags},
232  IsHostVisible {_IsHostVisible}
233  {}
234 
235  bool operator == (const MemoryPageIndex& rhs)const
236  {
237  return MemoryTypeIndex == rhs.MemoryTypeIndex &&
238  AllocateFlags == rhs.AllocateFlags &&
240  }
241  // clang-format on
242 
243  struct Hasher
244  {
245  size_t operator()(const MemoryPageIndex& PageIndex) const
246  {
247  return Diligent::ComputeHash(PageIndex.MemoryTypeIndex, PageIndex.AllocateFlags, PageIndex.IsHostVisible);
248  }
249  };
250  };
251  std::unordered_multimap<MemoryPageIndex, VulkanMemoryPage, MemoryPageIndex::Hasher> m_Pages;
252 
253  const VkDeviceSize m_DeviceLocalPageSize;
254  const VkDeviceSize m_HostVisiblePageSize;
255  const VkDeviceSize m_DeviceLocalReserveSize;
256  const VkDeviceSize m_HostVisibleReserveSize;
257 
258  void OnFreeAllocation(VkDeviceSize Size, bool IsHostVisble);
259 
260  // 0 == Device local, 1 == Host-visible
261  std::array<std::atomic_int64_t, 2> m_CurrUsedSize = {};
262  std::array<VkDeviceSize, 2> m_PeakUsedSize = {};
263  std::array<VkDeviceSize, 2> m_CurrAllocatedSize = {};
264  std::array<VkDeviceSize, 2> m_PeakAllocatedSize = {};
265 
266  // If adding new member, do not forget to update move ctor
267 };
268 
269 } // namespace VulkanUtilities
Diligent::VariableSizeAllocationsManager::IsEmpty
bool IsEmpty() const
Definition: VariableSizeAllocationsManager.hpp:356
VulkanUtilities::VulkanMemoryManager::m_HostVisibleReserveSize
const VkDeviceSize m_HostVisibleReserveSize
Definition: VulkanMemoryManager.hpp:256
VariableSizeAllocationsManager.hpp
VulkanLogicalDevice.hpp
VulkanUtilities::VulkanMemoryManager::m_PeakUsedSize
std::array< VkDeviceSize, 2 > m_PeakUsedSize
Definition: VulkanMemoryManager.hpp:262
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex
Definition: VulkanMemoryManager.hpp:220
Diligent::VariableSizeAllocationsManager::IsFull
bool IsFull() const
Definition: VariableSizeAllocationsManager.hpp:355
VulkanUtilities::VulkanMemoryPage::VulkanMemoryPage
VulkanMemoryPage(VulkanMemoryManager &ParentMemoryMgr, VkDeviceSize PageSize, uint32_t MemoryTypeIndex, bool IsHostVisible, VkMemoryAllocateFlags AllocateFlags) noexcept
Definition: VulkanMemoryManager.cpp:43
VulkanUtilities::VulkanMemoryManager::VulkanMemoryManager
VulkanMemoryManager(VulkanMemoryManager &&rhs) noexcept
Definition: VulkanMemoryManager.hpp:172
VulkanUtilities::VulkanMemoryManager::m_MgrName
std::string m_MgrName
Definition: VulkanMemoryManager.hpp:212
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::IsHostVisible
const bool IsHostVisible
Definition: VulkanMemoryManager.hpp:224
Diligent::VariableSizeAllocationsManager
Definition: VariableSizeAllocationsManager.hpp:64
VulkanUtilities::VulkanMemoryManager::m_Allocator
Diligent::IMemoryAllocator & m_Allocator
Definition: VulkanMemoryManager.hpp:217
VulkanUtilities::VulkanLogicalDevice
Definition: VulkanLogicalDevice.hpp:88
VulkanUtilities::VulkanMemoryAllocation::VulkanMemoryAllocation
VulkanMemoryAllocation() noexcept
Definition: VulkanMemoryManager.hpp:50
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::operator==
bool operator==(const MemoryPageIndex &rhs) const
Definition: VulkanMemoryManager.hpp:235
Diligent::VariableSizeAllocationsManager::GetMaxSize
OffsetType GetMaxSize() const
Definition: VariableSizeAllocationsManager.hpp:357
VulkanUtilities::VulkanMemoryManager::m_HostVisiblePageSize
const VkDeviceSize m_HostVisiblePageSize
Definition: VulkanMemoryManager.hpp:254
VulkanUtilities::DeviceMemoryWrapper
DEFINE_VULKAN_OBJECT_WRAPPER(DeviceMemory) DeviceMemoryWrapper
Definition: VulkanLogicalDevice.hpp:73
VulkanUtilities::VulkanMemoryManager::OnFreeAllocation
void OnFreeAllocation(VkDeviceSize Size, bool IsHostVisble)
Definition: VulkanMemoryManager.cpp:241
VulkanUtilities::VulkanMemoryManager::m_LogicalDevice
const VulkanLogicalDevice & m_LogicalDevice
Definition: VulkanMemoryManager.hpp:214
Diligent::VariableSizeAllocationsManager::GetUsedSize
OffsetType GetUsedSize() const
Definition: VariableSizeAllocationsManager.hpp:359
VulkanUtilities::VulkanMemoryManager::m_CurrAllocatedSize
std::array< VkDeviceSize, 2 > m_CurrAllocatedSize
Definition: VulkanMemoryManager.hpp:263
VulkanUtilities::VulkanMemoryManager::m_DeviceLocalPageSize
const VkDeviceSize m_DeviceLocalPageSize
Definition: VulkanMemoryManager.hpp:253
VulkanUtilities::VulkanMemoryManager::~VulkanMemoryManager
~VulkanMemoryManager()
Definition: VulkanMemoryManager.cpp:246
VulkanUtilities::VulkanMemoryAllocation::VulkanMemoryAllocation
VulkanMemoryAllocation(VulkanMemoryAllocation &&rhs) noexcept
Definition: VulkanMemoryManager.hpp:62
VulkanUtilities::VulkanMemoryManager::Allocate
VulkanMemoryAllocation Allocate(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex, bool HostVisible, VkMemoryAllocateFlags AllocateFlags)
Definition: VulkanMemoryManager.cpp:161
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::Hasher
Definition: VulkanMemoryManager.hpp:243
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::MemoryPageIndex
MemoryPageIndex(uint32_t _MemoryTypeIndex, bool _IsHostVisible, VkMemoryAllocateFlags _AllocateFlags)
Definition: VulkanMemoryManager.hpp:227
VulkanUtilities::VulkanMemoryAllocation::~VulkanMemoryAllocation
~VulkanMemoryAllocation()
Definition: VulkanMemoryManager.cpp:35
VulkanUtilities::VulkanMemoryPage::operator=
VulkanMemoryPage & operator=(VulkanMemoryPage &)=delete
VulkanUtilities::VulkanMemoryAllocation::UnalignedOffset
VkDeviceSize UnalignedOffset
Definition: VulkanMemoryManager.hpp:91
VulkanUtilities::VulkanMemoryManager::VulkanMemoryManager
VulkanMemoryManager(std::string MgrName, const VulkanLogicalDevice &LogicalDevice, const VulkanPhysicalDevice &PhysicalDevice, Diligent::IMemoryAllocator &Allocator, VkDeviceSize DeviceLocalPageSize, VkDeviceSize HostVisiblePageSize, VkDeviceSize DeviceLocalReserveSize, VkDeviceSize HostVisibleReserveSize)
Definition: VulkanMemoryManager.hpp:150
VulkanUtilities::VulkanMemoryAllocation::VulkanMemoryAllocation
VulkanMemoryAllocation(VulkanMemoryPage *_Page, VkDeviceSize _UnalignedOffset, VkDeviceSize _Size) noexcept
Definition: VulkanMemoryManager.hpp:56
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::MemoryTypeIndex
const uint32_t MemoryTypeIndex
Definition: VulkanMemoryManager.hpp:222
VulkanUtilities::VulkanMemoryManager::m_Pages
std::unordered_multimap< MemoryPageIndex, VulkanMemoryPage, MemoryPageIndex::Hasher > m_Pages
Definition: VulkanMemoryManager.hpp:251
Diligent::VariableSizeAllocationsManager::OffsetType
size_t OffsetType
Definition: VariableSizeAllocationsManager.hpp:67
VulkanUtilities::VulkanMemoryPage::~VulkanMemoryPage
~VulkanMemoryPage()
Definition: VulkanMemoryManager.cpp:88
VulkanUtilities::VulkanMemoryManager::m_CurrUsedSize
std::array< std::atomic_int64_t, 2 > m_CurrUsedSize
Definition: VulkanMemoryManager.hpp:261
VulkanUtilities::VulkanMemoryManager::OnNewPageCreated
virtual void OnNewPageCreated(VulkanMemoryPage &NewPage)
Definition: VulkanMemoryManager.hpp:209
VulkanUtilities::VulkanMemoryManager::OnPageDestroy
virtual void OnPageDestroy(VulkanMemoryPage &Page)
Definition: VulkanMemoryManager.hpp:210
VulkanUtilities::VulkanMemoryPage::IsEmpty
bool IsEmpty() const
Definition: VulkanMemoryManager.hpp:119
Diligent::ComputeHash
std::size_t ComputeHash(const ArgsType &... Args)
Definition: HashUtils.hpp:57
VulkanUtilities::VulkanMemoryPage::GetVkMemory
VkDeviceMemory GetVkMemory() const
Definition: VulkanMemoryManager.hpp:128
VulkanUtilities::VulkanMemoryManager::m_PhysicalDevice
const VulkanPhysicalDevice & m_PhysicalDevice
Definition: VulkanMemoryManager.hpp:215
VulkanUtilities::VulkanMemoryManager::m_DeviceLocalReserveSize
const VkDeviceSize m_DeviceLocalReserveSize
Definition: VulkanMemoryManager.hpp:255
VulkanUtilities::VulkanMemoryPage
Definition: VulkanMemoryManager.hpp:95
VulkanUtilities::VulkanMemoryManager::m_PagesMtx
std::mutex m_PagesMtx
Definition: VulkanMemoryManager.hpp:219
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::Hasher::operator()
size_t operator()(const MemoryPageIndex &PageIndex) const
Definition: VulkanMemoryManager.hpp:245
VulkanUtilities::VulkanMemoryPage::IsFull
bool IsFull() const
Definition: VulkanMemoryManager.hpp:120
VulkanUtilities::VulkanMemoryPage::GetPageSize
VkDeviceSize GetPageSize() const
Definition: VulkanMemoryManager.hpp:121
VulkanUtilities::VulkanMemoryAllocation::Size
VkDeviceSize Size
Definition: VulkanMemoryManager.hpp:92
VulkanUtilities::VulkanMemoryManager::ShrinkMemory
void ShrinkMemory()
Definition: VulkanMemoryManager.cpp:213
VulkanUtilities::VulkanMemoryPage::GetUsedSize
VkDeviceSize GetUsedSize() const
Definition: VulkanMemoryManager.hpp:122
VulkanUtilities::VulkanPhysicalDevice
Definition: VulkanPhysicalDevice.hpp:37
Diligent::IMemoryAllocator
Base interface for a raw memory allocator.
Definition: MemoryAllocator.h:41
VulkanUtilities::VulkanMemoryManager::MemoryPageIndex::AllocateFlags
const VkMemoryAllocateFlags AllocateFlags
Definition: VulkanMemoryManager.hpp:223
HashUtils.hpp
MemoryAllocator.h
VulkanUtilities::VulkanMemoryAllocation::Page
VulkanMemoryPage * Page
Definition: VulkanMemoryManager.hpp:90
VulkanUtilities::VulkanMemoryManager
Definition: VulkanMemoryManager.hpp:146
VulkanUtilities::VulkanMemoryPage::VulkanMemoryPage
VulkanMemoryPage(VulkanMemoryPage &&rhs) noexcept
Definition: VulkanMemoryManager.hpp:106
VulkanObjectWrappers.hpp
VulkanUtilities::VulkanMemoryManager::m_PeakAllocatedSize
std::array< VkDeviceSize, 2 > m_PeakAllocatedSize
Definition: VulkanMemoryManager.hpp:264
VulkanUtilities
Definition: VulkanCommandBuffer.hpp:33
VulkanUtilities::VulkanMemoryAllocation::operator=
VulkanMemoryAllocation & operator=(const VulkanMemoryAllocation &)=delete
VulkanUtilities::VulkanMemoryPage::Allocate
VulkanMemoryAllocation Allocate(VkDeviceSize size, VkDeviceSize alignment)
Definition: VulkanMemoryManager.cpp:99
VulkanUtilities::VulkanMemoryManager::operator=
VulkanMemoryManager & operator=(const VulkanMemoryManager &)=delete
VulkanUtilities::VulkanMemoryPage::GetCPUMemory
void * GetCPUMemory() const
Definition: VulkanMemoryManager.hpp:129
VulkanPhysicalDevice.hpp
VulkanUtilities::VulkanMemoryAllocation
Definition: VulkanMemoryManager.hpp:48