• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2022 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Suballocation.h:
7 //    Defines class interface for BufferBlock and Suballocation and other related classes.
8 //
9 
10 #ifndef LIBANGLE_RENDERER_VULKAN_SUBALLOCATION_H_
11 #define LIBANGLE_RENDERER_VULKAN_SUBALLOCATION_H_
12 
13 #include "common/SimpleMutex.h"
14 #include "common/debug.h"
15 #include "libANGLE/angletypes.h"
16 #include "libANGLE/renderer/serial_utils.h"
17 #include "libANGLE/renderer/vulkan/vk_cache_utils.h"
18 #include "libANGLE/renderer/vulkan/vk_resource.h"
19 #include "libANGLE/renderer/vulkan/vk_utils.h"
20 #include "libANGLE/renderer/vulkan/vk_wrapper.h"
21 
22 namespace rx
23 {
24 enum class MemoryAllocationType;
25 
26 namespace vk
27 {
28 class ErrorContext;
29 
30 // BufferBlock
31 class BufferBlock final : angle::NonCopyable
32 {
33   public:
34     BufferBlock();
35     BufferBlock(BufferBlock &&other);
36     ~BufferBlock();
37 
38     void destroy(Renderer *renderer);
39     VkResult init(ErrorContext *context,
40                   Buffer &buffer,
41                   uint32_t memoryTypeIndex,
42                   vma::VirtualBlockCreateFlags flags,
43                   DeviceMemory &deviceMemory,
44                   VkMemoryPropertyFlags memoryPropertyFlags,
45                   VkDeviceSize size);
46     void initWithoutVirtualBlock(ErrorContext *context,
47                                  Buffer &buffer,
48                                  MemoryAllocationType memoryAllocationType,
49                                  uint32_t memoryTypeIndex,
50                                  DeviceMemory &deviceMemory,
51                                  VkMemoryPropertyFlags memoryPropertyFlags,
52                                  VkDeviceSize size,
53                                  VkDeviceSize allocatedBufferSize);
54 
55     BufferBlock &operator=(BufferBlock &&other);
56 
getBuffer()57     const Buffer &getBuffer() const { return mBuffer; }
getDeviceMemory()58     const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
getDeviceMemory()59     DeviceMemory &getDeviceMemory() { return mDeviceMemory; }
getBufferSerial()60     BufferSerial getBufferSerial() const { return mSerial; }
61 
62     VkMemoryPropertyFlags getMemoryPropertyFlags() const;
63     VkDeviceSize getMemorySize() const;
64     VkDeviceSize getAllocatedBufferSize() const;
65 
66     VkResult allocate(VkDeviceSize size,
67                       VkDeviceSize alignment,
68                       VmaVirtualAllocation *allocationOut,
69                       VkDeviceSize *offsetOut);
70     void free(VmaVirtualAllocation allocation, VkDeviceSize offset);
71     VkBool32 isEmpty();
72 
hasVirtualBlock()73     bool hasVirtualBlock() const { return mVirtualBlock.valid(); }
74     bool isHostVisible() const;
75     bool isCoherent() const;
76     bool isCached() const;
77     bool isMapped() const;
78     VkResult map(const VkDevice device);
79     void unmap(const VkDevice device);
80     uint8_t *getMappedMemory() const;
81 
82     // This should be called whenever this found to be empty. The total number of count of empty is
83     // returned.
84     int32_t getAndIncrementEmptyCounter();
85     void calculateStats(vma::StatInfo *pStatInfo) const;
86 
87   private:
88     mutable angle::SimpleMutex mVirtualBlockMutex;
89     VirtualBlock mVirtualBlock;
90 
91     Buffer mBuffer;
92     DeviceMemory mDeviceMemory;
93     VkMemoryPropertyFlags mMemoryPropertyFlags;
94 
95     // Memory size that user of this object thinks we have.
96     VkDeviceSize mSize;
97     // Memory size that was actually allocated for this object.
98     VkDeviceSize mAllocatedBufferSize;
99     // Memory allocation type used for this object.
100     MemoryAllocationType mMemoryAllocationType;
101     // Memory type index used for the allocation. It can be used to determine the heap index.
102     uint32_t mMemoryTypeIndex;
103 
104     uint8_t *mMappedMemory;
105     BufferSerial mSerial;
106     // Heuristic information for pruneEmptyBuffer. This tracks how many times (consecutively) this
107     // buffer block is found to be empty when pruneEmptyBuffer is called. This gets reset whenever
108     // it becomes non-empty.
109     int32_t mCountRemainsEmpty;
110 };
111 using BufferBlockPointer       = std::unique_ptr<BufferBlock>;
112 using BufferBlockPointerVector = std::vector<BufferBlockPointer>;
113 
114 class BufferBlockGarbageList final : angle::NonCopyable
115 {
116   public:
BufferBlockGarbageList()117     BufferBlockGarbageList() : mBufferBlockQueue(kInitialQueueCapacity) {}
~BufferBlockGarbageList()118     ~BufferBlockGarbageList() { ASSERT(mBufferBlockQueue.empty()); }
119 
add(BufferBlock * bufferBlock)120     void add(BufferBlock *bufferBlock)
121     {
122         std::unique_lock<angle::SimpleMutex> lock(mMutex);
123         if (mBufferBlockQueue.full())
124         {
125             size_t newCapacity = mBufferBlockQueue.capacity() << 1;
126             mBufferBlockQueue.updateCapacity(newCapacity);
127         }
128         mBufferBlockQueue.push(bufferBlock);
129     }
130 
131     // Number of buffer blocks destroyed is returned.
pruneEmptyBufferBlocks(Renderer * renderer)132     size_t pruneEmptyBufferBlocks(Renderer *renderer)
133     {
134         size_t blocksDestroyed = 0;
135         if (!mBufferBlockQueue.empty())
136         {
137             std::unique_lock<angle::SimpleMutex> lock(mMutex);
138             size_t count = mBufferBlockQueue.size();
139             for (size_t i = 0; i < count; i++)
140             {
141                 BufferBlock *block = mBufferBlockQueue.front();
142                 mBufferBlockQueue.pop();
143                 if (block->isEmpty())
144                 {
145                     block->destroy(renderer);
146                     ++blocksDestroyed;
147                 }
148                 else
149                 {
150                     mBufferBlockQueue.push(block);
151                 }
152             }
153         }
154         return blocksDestroyed;
155     }
156 
empty()157     bool empty() const { return mBufferBlockQueue.empty(); }
158 
159   private:
160     static constexpr size_t kInitialQueueCapacity = 4;
161     angle::SimpleMutex mMutex;
162     angle::FixedQueue<BufferBlock *> mBufferBlockQueue;
163 };
164 
165 // BufferSuballocation
166 class BufferSuballocation final : angle::NonCopyable
167 {
168   public:
169     BufferSuballocation();
170 
171     BufferSuballocation(BufferSuballocation &&other);
172     BufferSuballocation &operator=(BufferSuballocation &&other);
173 
174     void destroy(Renderer *renderer);
175 
176     void init(BufferBlock *block,
177               VmaVirtualAllocation allocation,
178               VkDeviceSize offset,
179               VkDeviceSize size);
180     void initWithEntireBuffer(ErrorContext *context,
181                               Buffer &buffer,
182                               MemoryAllocationType memoryAllocationType,
183                               uint32_t memoryTypeIndex,
184                               DeviceMemory &deviceMemory,
185                               VkMemoryPropertyFlags memoryPropertyFlags,
186                               VkDeviceSize size,
187                               VkDeviceSize allocatedBufferSize);
188 
189     const Buffer &getBuffer() const;
190     VkDeviceSize getSize() const;
191     const DeviceMemory &getDeviceMemory() const;
192     VkMemoryMapFlags getMemoryPropertyFlags() const;
193     bool isHostVisible() const;
194     bool isCoherent() const;
195     bool isCached() const;
196     bool isMapped() const;
197     uint8_t *getMappedMemory() const;
198     void flush(Renderer *renderer);
199     void invalidate(Renderer *renderer);
200     VkDeviceSize getOffset() const;
201     bool valid() const;
202     VkResult map(ErrorContext *context);
203     BufferSerial getBlockSerial() const;
204     uint8_t *getBlockMemory() const;
205     VkDeviceSize getBlockMemorySize() const;
isSuballocated()206     bool isSuballocated() const { return mBufferBlock->hasVirtualBlock(); }
getBufferBlock()207     BufferBlock *getBufferBlock() const { return mBufferBlock; }
208 
209   private:
210     // Only used by DynamicBuffer where DynamicBuffer does the actual suballocation and pass the
211     // offset/size to this object. Since DynamicBuffer does not have a VMA virtual allocator, they
212     // will be ignored at destroy time. The offset/size is set here mainly for easy retrieval when
213     // the BufferHelper object is passed around.
214     friend class BufferHelper;
215     void setOffsetAndSize(VkDeviceSize offset, VkDeviceSize size);
216 
217     BufferBlock *mBufferBlock;
218     VmaVirtualAllocation mAllocation;
219     VkDeviceSize mOffset;
220     VkDeviceSize mSize;
221 };
222 
223 class BufferSuballocationGarbage
224 {
225   public:
226     BufferSuballocationGarbage() = default;
BufferSuballocationGarbage(BufferSuballocationGarbage && other)227     BufferSuballocationGarbage(BufferSuballocationGarbage &&other)
228         : mLifetime(other.mLifetime),
229           mSuballocation(std::move(other.mSuballocation)),
230           mBuffer(std::move(other.mBuffer))
231     {}
232     BufferSuballocationGarbage &operator=(BufferSuballocationGarbage &&other)
233     {
234         mLifetime      = other.mLifetime;
235         mSuballocation = std::move(other.mSuballocation);
236         mBuffer        = std::move(other.mBuffer);
237         return *this;
238     }
BufferSuballocationGarbage(const ResourceUse & use,BufferSuballocation && suballocation,Buffer && buffer)239     BufferSuballocationGarbage(const ResourceUse &use,
240                                BufferSuballocation &&suballocation,
241                                Buffer &&buffer)
242         : mLifetime(use), mSuballocation(std::move(suballocation)), mBuffer(std::move(buffer))
243     {}
244     ~BufferSuballocationGarbage() = default;
245 
246     bool destroyIfComplete(Renderer *renderer);
247     bool hasResourceUseSubmitted(Renderer *renderer) const;
getSize()248     VkDeviceSize getSize() const { return mSuballocation.getSize(); }
isSuballocated()249     bool isSuballocated() const { return mSuballocation.isSuballocated(); }
250 
251   private:
252     ResourceUse mLifetime;
253     BufferSuballocation mSuballocation;
254     Buffer mBuffer;
255 };
256 
257 // BufferBlock implementation.
getMemoryPropertyFlags()258 ANGLE_INLINE VkMemoryPropertyFlags BufferBlock::getMemoryPropertyFlags() const
259 {
260     return mMemoryPropertyFlags;
261 }
262 
getMemorySize()263 ANGLE_INLINE VkDeviceSize BufferBlock::getMemorySize() const
264 {
265     return mSize;
266 }
267 
getAllocatedBufferSize()268 ANGLE_INLINE VkDeviceSize BufferBlock::getAllocatedBufferSize() const
269 {
270     return mAllocatedBufferSize;
271 }
272 
isEmpty()273 ANGLE_INLINE VkBool32 BufferBlock::isEmpty()
274 {
275     std::unique_lock<angle::SimpleMutex> lock(mVirtualBlockMutex);
276     return vma::IsVirtualBlockEmpty(mVirtualBlock.getHandle());
277 }
278 
isHostVisible()279 ANGLE_INLINE bool BufferBlock::isHostVisible() const
280 {
281     return (mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
282 }
283 
isCoherent()284 ANGLE_INLINE bool BufferBlock::isCoherent() const
285 {
286     return (mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
287 }
288 
isCached()289 ANGLE_INLINE bool BufferBlock::isCached() const
290 {
291     return (mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0;
292 }
293 
isMapped()294 ANGLE_INLINE bool BufferBlock::isMapped() const
295 {
296     return mMappedMemory != nullptr;
297 }
298 
getMappedMemory()299 ANGLE_INLINE uint8_t *BufferBlock::getMappedMemory() const
300 {
301     ASSERT(mMappedMemory != nullptr);
302     return mMappedMemory;
303 }
304 
305 // BufferSuballocation implementation.
BufferSuballocation()306 ANGLE_INLINE BufferSuballocation::BufferSuballocation()
307     : mBufferBlock(nullptr), mAllocation(VK_NULL_HANDLE), mOffset(0), mSize(0)
308 {}
309 
BufferSuballocation(BufferSuballocation && other)310 ANGLE_INLINE BufferSuballocation::BufferSuballocation(BufferSuballocation &&other)
311     : BufferSuballocation()
312 {
313     *this = std::move(other);
314 }
315 
316 ANGLE_INLINE BufferSuballocation &BufferSuballocation::operator=(BufferSuballocation &&other)
317 {
318     std::swap(mBufferBlock, other.mBufferBlock);
319     std::swap(mSize, other.mSize);
320     std::swap(mAllocation, other.mAllocation);
321     std::swap(mOffset, other.mOffset);
322     return *this;
323 }
324 
valid()325 ANGLE_INLINE bool BufferSuballocation::valid() const
326 {
327     return mBufferBlock != nullptr;
328 }
329 
destroy(Renderer * renderer)330 ANGLE_INLINE void BufferSuballocation::destroy(Renderer *renderer)
331 {
332     if (valid())
333     {
334         ASSERT(mBufferBlock);
335         if (mBufferBlock->hasVirtualBlock())
336         {
337             mBufferBlock->free(mAllocation, mOffset);
338             mBufferBlock = nullptr;
339         }
340         else
341         {
342             // When virtual block is invalid, this is the standalone buffer that are created by
343             // BufferSuballocation::initWithEntireBuffer call. In this case, vmaBufferSuballocation
344             // owns block, we must properly delete the block object.
345             mBufferBlock->destroy(renderer);
346             SafeDelete(mBufferBlock);
347         }
348         mAllocation = VK_NULL_HANDLE;
349         mOffset     = 0;
350         mSize       = 0;
351     }
352 }
353 
init(BufferBlock * block,VmaVirtualAllocation allocation,VkDeviceSize offset,VkDeviceSize size)354 ANGLE_INLINE void BufferSuballocation::init(BufferBlock *block,
355                                             VmaVirtualAllocation allocation,
356                                             VkDeviceSize offset,
357                                             VkDeviceSize size)
358 {
359     ASSERT(!valid());
360     ASSERT(block != nullptr);
361     ASSERT(allocation != VK_NULL_HANDLE);
362     ASSERT(offset != VK_WHOLE_SIZE);
363     mBufferBlock = block;
364     mAllocation  = allocation;
365     mOffset      = offset;
366     mSize        = size;
367 }
368 
initWithEntireBuffer(ErrorContext * context,Buffer & buffer,MemoryAllocationType memoryAllocationType,uint32_t memoryTypeIndex,DeviceMemory & deviceMemory,VkMemoryPropertyFlags memoryPropertyFlags,VkDeviceSize size,VkDeviceSize allocatedBufferSize)369 ANGLE_INLINE void BufferSuballocation::initWithEntireBuffer(
370     ErrorContext *context,
371     Buffer &buffer,
372     MemoryAllocationType memoryAllocationType,
373     uint32_t memoryTypeIndex,
374     DeviceMemory &deviceMemory,
375     VkMemoryPropertyFlags memoryPropertyFlags,
376     VkDeviceSize size,
377     VkDeviceSize allocatedBufferSize)
378 {
379     ASSERT(!valid());
380 
381     std::unique_ptr<BufferBlock> block = std::make_unique<BufferBlock>();
382     block->initWithoutVirtualBlock(context, buffer, memoryAllocationType, memoryTypeIndex,
383                                    deviceMemory, memoryPropertyFlags, size, allocatedBufferSize);
384 
385     mBufferBlock = block.release();
386     mAllocation  = VK_NULL_HANDLE;
387     mOffset      = 0;
388     mSize        = mBufferBlock->getMemorySize();
389 }
390 
getBuffer()391 ANGLE_INLINE const Buffer &BufferSuballocation::getBuffer() const
392 {
393     return mBufferBlock->getBuffer();
394 }
395 
getSize()396 ANGLE_INLINE VkDeviceSize BufferSuballocation::getSize() const
397 {
398     return mSize;
399 }
400 
getDeviceMemory()401 ANGLE_INLINE const DeviceMemory &BufferSuballocation::getDeviceMemory() const
402 {
403     return mBufferBlock->getDeviceMemory();
404 }
405 
getMemoryPropertyFlags()406 ANGLE_INLINE VkMemoryMapFlags BufferSuballocation::getMemoryPropertyFlags() const
407 {
408     return mBufferBlock->getMemoryPropertyFlags();
409 }
410 
isHostVisible()411 ANGLE_INLINE bool BufferSuballocation::isHostVisible() const
412 {
413     return mBufferBlock->isHostVisible();
414 }
isCoherent()415 ANGLE_INLINE bool BufferSuballocation::isCoherent() const
416 {
417     return mBufferBlock->isCoherent();
418 }
isCached()419 ANGLE_INLINE bool BufferSuballocation::isCached() const
420 {
421     return mBufferBlock->isCached();
422 }
isMapped()423 ANGLE_INLINE bool BufferSuballocation::isMapped() const
424 {
425     return mBufferBlock->isMapped();
426 }
getMappedMemory()427 ANGLE_INLINE uint8_t *BufferSuballocation::getMappedMemory() const
428 {
429     return mBufferBlock->getMappedMemory() + getOffset();
430 }
431 
getOffset()432 ANGLE_INLINE VkDeviceSize BufferSuballocation::getOffset() const
433 {
434     return mOffset;
435 }
436 
setOffsetAndSize(VkDeviceSize offset,VkDeviceSize size)437 ANGLE_INLINE void BufferSuballocation::setOffsetAndSize(VkDeviceSize offset, VkDeviceSize size)
438 {
439     mOffset = offset;
440     mSize   = size;
441 }
442 
getBlockMemory()443 ANGLE_INLINE uint8_t *BufferSuballocation::getBlockMemory() const
444 {
445     return mBufferBlock->getMappedMemory();
446 }
getBlockMemorySize()447 ANGLE_INLINE VkDeviceSize BufferSuballocation::getBlockMemorySize() const
448 {
449     return mBufferBlock->getMemorySize();
450 }
getBlockSerial()451 ANGLE_INLINE BufferSerial BufferSuballocation::getBlockSerial() const
452 {
453     ASSERT(valid());
454     return mBufferBlock->getBufferSerial();
455 }
456 }  // namespace vk
457 }  // namespace rx
458 
459 #endif  // LIBANGLE_RENDERER_VULKAN_SUBALLOCATION_H_
460