Nebula
Loading...
Searching...
No Matches
rangeallocator.h
Go to the documentation of this file.
1#pragma once
2//------------------------------------------------------------------------------
9//------------------------------------------------------------------------------
10#include "core/types.h"
11#include "util/bit.h"
12#include "util/array.h"
13#include <utility>
14
15namespace Memory
16{
17
18//------------------------------------------------------------------------------
24struct RangeAllocation
25{
29
30 static constexpr uint OOM = 0xFFFFFFFF;
31};
32
33//------------------------------------------------------------------------------
46{
47public:
51 RangeAllocator(uint size, SizeT maxNumAllocs);
54
56 void Clear();
57
59 bool Empty();
60
62 RangeAllocation Alloc(uint size, uint alignment = 1);
64 void Dealloc(const RangeAllocation& allocation);
65
66private:
68 uint InsertNode(uint size, uint offset);
70 void RemoveNode(uint nodeIndex);
71
73 {
75 resident(false)
76 , size(0)
77 , offset(0)
78 , binPrev(END)
79 , binNext(END)
80 , blockPrev(END)
81 , blockNext(END)
82 {};
86
87 static constexpr uint END = 0xFFFFFFFF;
90 };
91
93 {
94 BinIndex() : index(0) {};
95 struct
96 {
99 };
101 };
102
104 static BinIndex IndexFromSize(uint size, bool round = false);
108 static uint BinFromSize(uint size, uint bucket);
109
113
114 static constexpr uint NUM_BUCKETS = 0x20;
115 static constexpr uint NUM_BINS_PER_BUCKET = 0x10;
116
120
123};
124
125//------------------------------------------------------------------------------
128inline
133
134//------------------------------------------------------------------------------
137inline
139{
140 this->size = size;
141 this->freeNodes.Resize(maxNumAllocs);
142 this->nodes.Resize(maxNumAllocs);
143
144 // Clear the allocator
145 this->Clear();
146}
147
148//------------------------------------------------------------------------------
151inline
155
156//------------------------------------------------------------------------------
159inline void
161{
162 // Reset bit masks
163 this->bucketUsageMask = 0x0;
164 memset(this->binMasks, 0x0, sizeof(this->binMasks));
165
166 // Reset bin heads
167 for (uint i = 0; i < NUM_BUCKETS * NUM_BINS_PER_BUCKET; i++)
168 {
170 }
171
172 // Clear nodes
173 for (uint i = 0; i < this->nodes.Size(); i++)
174 {
175 this->freeNodes[i] = this->nodes.Size() - i - 1;
176 }
177 this->freeNodeIterator = this->nodes.Size() - 1;
178 this->freeStorage = 0;
179
180 // Insert node covering the whole range
181 if (this->size > 0)
182 this->InsertNode(this->size, 0);
183}
184
185//------------------------------------------------------------------------------
188inline bool
190{
191 return this->freeStorage == this->size;
192}
193
194//------------------------------------------------------------------------------
202inline RangeAllocation
204{
205 // We are not allowed any more allocations
206 uint alignedSize = size + alignment - 1;
207 if (this->freeStorage < alignedSize)
208 {
209 return RangeAllocation{ .offset = RangeAllocation::OOM, .size = 0, .node = RangeAllocatorNode::END };
210 }
211
212 BinIndex minIndex = IndexFromSize(alignedSize, true);
213 uint bin = 0xFFFFFFFF;
214 uint bucket = minIndex.bucket;
215
216 if (this->bucketUsageMask & (1 << bucket))
217 {
218 bin = Util::Lsb(this->binMasks[bucket], minIndex.bin);
219 }
220
221 if (bin == 0xFFFFFFFF)
222 {
223 // Find next bit set after bucket in the usage mask
224 bucket = Util::Lsb(this->bucketUsageMask, bucket + 1);
225
226 // If this means we get 32, it means we're out of buckets that fit
227 if (bucket == 0xFFFFFFFF)
228 {
229 return RangeAllocation{ .offset = RangeAllocation::OOM, .size = 0, .node = RangeAllocatorNode::END };
230 }
231
232 // Find any bit, since this bucket has to fit
233 bin = Util::FirstOne(this->binMasks[bucket]);
234 }
235
236 BinIndex binIndex;
237 binIndex.bucket = bucket;
238 binIndex.bin = bin;
239
240 // Get linked list head
241 uint nodeIndex = this->binHeads[binIndex.index];
242 RangeAllocatorNode& node = this->nodes[nodeIndex];
243 n_assert(!node.resident);
244
245 // Save total size of node
246 uint totalSize = node.size;
247 uint baseOffset = node.offset;
248 node.size = alignedSize;
249 node.resident = true;
250 node.offset = Math::align(node.offset, alignment);
251
252 // Bump head of bin to next node
253 this->binHeads[binIndex.index] = node.binNext;
255 this->nodes[node.binNext].binPrev = RangeAllocatorNode::END;
256
257 // If bin head is empty after we grab the node, unmark the bin
258 if (this->binHeads[binIndex.index] == RangeAllocatorNode::END)
259 {
260 this->binMasks[binIndex.bucket] &= ~(1 << binIndex.bin);
261
262 // If all bins are busy, unmark the bucket bits
263 if (this->binMasks[binIndex.bucket] == 0)
264 {
265 this->bucketUsageMask &= ~(1 << binIndex.bucket);
266 }
267 }
268
269 // We subtract the entire size of the node
270 this->freeStorage -= totalSize;
271
272 // The remainder in the front is then added back
273 uint remainder = totalSize - node.size;
274 if (remainder > 0)
275 {
276 uint newNodeIndex = this->InsertNode(remainder, node.offset + size);
277 if (newNodeIndex != RangeAllocatorNode::END)
278 {
279 RangeAllocatorNode& newNode = this->nodes[newNodeIndex];
280
281 // If the current node has a next, repoint it to the new block
283 {
284 this->nodes[node.blockNext].blockPrev = newNodeIndex;
285 }
286 newNode.blockPrev = nodeIndex;
287 newNode.blockNext = node.blockNext;
288 node.blockNext = newNodeIndex;
289 }
290 }
291
292 return RangeAllocation{ .offset = node.offset, .size = alignedSize, .node = nodeIndex };
293}
294
295//------------------------------------------------------------------------------
298inline void
300{
301 RangeAllocatorNode& node = this->nodes[allocation.node];
302 n_assert(node.resident);
303 node.resident = false;
304
305 uint size = node.size;
306 uint offset = node.offset;
307
308 // Try to merge with left
310 {
311 RangeAllocatorNode& prev = this->nodes[node.blockPrev];
312 if (!prev.resident)
313 {
314 offset = prev.offset;
315 size += prev.size;
316
317 this->RemoveNode(node.blockPrev);
318 node.blockPrev = prev.blockPrev;
319 }
320 }
321
323 {
324 RangeAllocatorNode& next = this->nodes[node.blockNext];
325 if (!next.resident)
326 {
327 size += next.size;
328 this->RemoveNode(node.blockNext);
329 node.blockNext = next.blockNext;
330 }
331 }
332
333 uint leftNode = node.blockPrev;
334 uint rightNode = node.blockNext;
335 this->freeNodes[++this->freeNodeIterator] = allocation.node;
336
337 // Add merged node
338 uint mergedNode = this->InsertNode(size, offset);
339
340 // Connect adjacent nodes with newly inserted node
341 if (leftNode != RangeAllocatorNode::END)
342 {
343 this->nodes[leftNode].blockNext = mergedNode;
344 this->nodes[mergedNode].blockPrev = leftNode;
345 }
346
347 if (rightNode != RangeAllocatorNode::END)
348 {
349 this->nodes[rightNode].blockPrev = mergedNode;
350 this->nodes[mergedNode].blockNext = rightNode;
351 }
352}
353
354//------------------------------------------------------------------------------
357inline uint
359{
360 if (this->freeNodeIterator == 0xFFFFFFFF)
362
363 BinIndex index = IndexFromSize(size);
364
365 if (this->binHeads[index.index] == RangeAllocatorNode::END)
366 {
367 // Bin isn't used, flip bit for bucket and bin
368 this->binMasks[index.bucket] |= (1 << index.bin);
369 this->bucketUsageMask |= (1 << index.bucket);
370 }
371
372 // Get new node
373 uint headNodeIndex = this->binHeads[index.index];
374 uint newNodeIndex = this->freeNodes[this->freeNodeIterator--];
375 RangeAllocatorNode& newNode = this->nodes[newNodeIndex];
376
377 // Set the size and offset of the node memory
378 newNode.size = size;
379 newNode.offset = offset;
380 newNode.binNext = headNodeIndex;
382 newNode.resident = false;
383
384 this->freeStorage += size;
385
386 // Get node at head of bin
387 if (headNodeIndex != RangeAllocatorNode::END)
388 {
389 // If valid, repoint head node to point to the new node in the bin
390 RangeAllocatorNode& headNode = this->nodes[headNodeIndex];
391 headNode.binPrev = newNodeIndex;
392 }
393 this->binHeads[index.index] = newNodeIndex;
394
395 return newNodeIndex;
396}
397
398//------------------------------------------------------------------------------
401inline void
403{
404 // Get node to free
405 RangeAllocatorNode& node = this->nodes[nodeIndex];
406 node.resident = false;
407
408 // Add back the storage to the allocator
409 this->freeStorage -= node.size;
410
412 {
413 BinIndex index = IndexFromSize(node.size);
414
415 // Point head of
416 this->binHeads[index.index] = node.binNext;
418 {
419 this->nodes[node.binNext].binPrev = RangeAllocatorNode::END;
420 }
421
423 {
424 // If we are deleting the last node, make sure to unmark the bin
425 this->binMasks[index.bucket] &= ~(1 << index.bin);
426
427 if (this->binMasks[index.bucket] == 0x0)
428 {
429 // If all bins have been cleared, clear the entire bucket
430 this->bucketUsageMask &= ~(1 << index.bucket);
431 }
432 }
433 }
434 else
435 {
436 this->nodes[node.binPrev].binNext = node.binNext;
438 {
439 this->nodes[node.binNext].binPrev = node.binPrev;
440 }
441 }
442
443 this->freeNodes[++this->freeNodeIterator] = nodeIndex;
444}
445
446//------------------------------------------------------------------------------
449inline uint
451{
452#if __WIN32__
453 DWORD count = 0;
454 _BitScanReverse(&count, size);
455#else
456 int count = 31 - __builtin_clz(size);
457#endif
458 return count;
459}
460
461//------------------------------------------------------------------------------
464inline uint
466{
467 uint mask = (size >> (bucket - 4)) & (NUM_BINS_PER_BUCKET - 1u);
468 return mask;
469}
470
471//------------------------------------------------------------------------------
476{
479 ret.bin = BinFromSize(size, ret.bucket);
480 if (round)
481 {
482 uint mask = (1 << ret.bucket) | (ret.bin << (ret.bucket - 4));
483 if ((~mask & size) != 0)
484 ret.bin++;
485 }
486 return ret;
487}
488
489} // namespace Memory
Implements helper functions for checking bits.
Allocates memory ranges using the TLSF method, with extended handling of padding to better suit GPUs.
~RangeAllocator()
Destructor.
Definition rangeallocator.h:152
RangeAllocator()
Default constructor.
Definition rangeallocator.h:129
void Clear()
Clears the allocator.
Definition rangeallocator.h:160
uint size
Definition rangeallocator.h:110
static uint BinFromSize(uint size, uint bucket)
Get bin from index.
Definition rangeallocator.h:465
Util::Array< RangeAllocatorNode > nodes
Definition rangeallocator.h:121
void RemoveNode(uint nodeIndex)
Remove node from bin.
Definition rangeallocator.h:402
uint InsertNode(uint size, uint offset)
Insert node at bin location, return node index.
Definition rangeallocator.h:358
static constexpr uint NUM_BUCKETS
Definition rangeallocator.h:114
static uint BucketFromSize(uint size)
Get bucket from index.
Definition rangeallocator.h:450
static constexpr uint NUM_BINS_PER_BUCKET
Definition rangeallocator.h:115
uint bucketUsageMask
Definition rangeallocator.h:117
bool Empty()
Checks if the allocator is empty.
Definition rangeallocator.h:189
uint freeNodeIterator
Definition rangeallocator.h:112
static BinIndex IndexFromSize(uint size, bool round=false)
Get bin index from size.
Definition rangeallocator.h:475
RangeAllocation Alloc(uint size, uint alignment=1)
Allocate range.
Definition rangeallocator.h:203
uint binMasks[NUM_BUCKETS]
Definition rangeallocator.h:118
uint freeStorage
Definition rangeallocator.h:111
Util::Array< uint > freeNodes
Definition rangeallocator.h:122
void Dealloc(const RangeAllocation &allocation)
Deallocate range.
Definition rangeallocator.h:299
uint binHeads[NUM_BUCKETS *NUM_BINS_PER_BUCKET]
Definition rangeallocator.h:119
Nebula's dynamic array class.
Definition array.h:60
void Resize(SizeT num)
Set size. Grows array if num is greater than capacity. Calls destroy on all objects at index > num!
Definition array.h:1606
const SizeT Size() const
get number of elements in array
Definition array.h:880
#define n_assert(exp)
Definition debug.h:50
__forceinline unsigned int align(unsigned int alignant, unsigned int alignment)
Definition scalar.h:722
Definition arenaallocator.h:31
uint Lsb(uint value, byte bit)
Definition bit.h:229
uint FirstOne(uint value)
Definition bit.h:169
Describes a range allocated by the Memory::RangeAllocator.
uint offset
Definition rangeallocator.h:26
static constexpr uint OOM
Definition rangeallocator.h:30
uint size
Definition rangeallocator.h:27
uint node
Definition rangeallocator.h:28
Definition rangeallocator.h:73
uint offset
Definition rangeallocator.h:85
bool resident
Definition rangeallocator.h:83
static constexpr uint END
Definition rangeallocator.h:87
uint blockNext
Definition rangeallocator.h:89
uint size
Definition rangeallocator.h:84
RangeAllocatorNode()
Definition rangeallocator.h:74
uint blockPrev
Definition rangeallocator.h:89
uint binPrev
Definition rangeallocator.h:88
uint binNext
Definition rangeallocator.h:88
int SizeT
Definition types.h:49
unsigned int uint
Definition types.h:31
uint16_t uint16
Definition types.h:40
Definition rangeallocator.h:93
uint16 index
Definition rangeallocator.h:100
uint16 bucket
Definition rangeallocator.h:98
BinIndex()
Definition rangeallocator.h:94
uint16 bin
Definition rangeallocator.h:97