Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
helios::memory::FrameAllocator Class Referencefinal

Linear allocator that clears every frame. More...

#include <frame_allocator.hpp>

Public Member Functions

 FrameAllocator (size_t capacity)
 Constructs a frame allocator with specified capacity.
 
 FrameAllocator (const FrameAllocator &)=delete
 
 FrameAllocator (FrameAllocator &&other) noexcept
 
 ~FrameAllocator () noexcept
 
FrameAllocatoroperator= (const FrameAllocator &)=delete
 
FrameAllocatoroperator= (FrameAllocator &&other) noexcept
 
AllocationResult Allocate (size_t size, size_t alignment=kDefaultAlignment) noexcept
 Allocates memory with specified size and alignment.
 
template<typename T >
T * Allocate () noexcept
 
template<typename T >
T * Allocate (size_t count) noexcept
 
template<typename T , typename... Args>
requires std::constructible_from<T, Args...>
T * AllocateAndConstruct (Args &&... args) noexcept(std::is_nothrow_constructible_v< T, Args... >)
 
template<typename T >
requires std::default_initializable<T>
T * AllocateAndConstructArray (size_t count) noexcept(std::is_nothrow_default_constructible_v< T >)
 
void Reset () noexcept
 Resets the allocator, freeing all allocations.
 
bool Empty () const noexcept
 Checks if the allocator is empty.
 
bool Full () const noexcept
 Checks if the allocator is full.
 
AllocatorStats Stats () const noexcept
 Gets current allocator statistics.
 
size_t Capacity () const noexcept
 Gets the total capacity of the allocator.
 
size_t CurrentOffset () const noexcept
 Gets the current offset (amount of memory used).
 
size_t FreeSpace () const noexcept
 Gets the amount of free space remaining.
 

Detailed Description

Linear allocator that clears every frame.

Fast bump-pointer allocator for per-frame temporary allocations. Extremely efficient for short-lived allocations that don't need individual deallocation. All memory is freed at once when Reset() is called (typically at frame end).

Uses atomic operations for allocation offset tracking.

Ideal for temporary data that lives for a single frame.

Note
Thread-safe. Deallocation is a no-op - memory is only freed on Reset().
Warning
Data allocated with this allocator is only valid until Reset() is called. All pointers and references to allocated memory become invalid after Reset(). Do not store frame-allocated data in persistent storage (components, resources, etc.).

Definition at line 35 of file frame_allocator.hpp.

Constructor & Destructor Documentation

◆ FrameAllocator() [1/3]

helios::memory::FrameAllocator::FrameAllocator ( size_t  capacity)
inlineexplicit

Constructs a frame allocator with specified capacity.

Warning
Triggers assertion if capacity is 0.
Parameters
capacityTotal size of the memory buffer in bytes
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 186 of file frame_allocator.hpp.

186 : capacity_(capacity) {
187 HELIOS_ASSERT(capacity > 0, "Failed to construct FrameAllocator: capacity must be greater than 0!");
188
189 // Allocate aligned buffer
190 buffer_ = AlignedAlloc(kDefaultAlignment, capacity_);
191 HELIOS_VERIFY(buffer_ != nullptr, "Failed to construct FrameAllocator: Allocation of buffer failed!");
192}
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:140
#define HELIOS_VERIFY(condition,...)
Verify macro that always checks the condition.
Definition assert.hpp:196
constexpr size_t kDefaultAlignment
Default alignment for allocations (cache line size for most modern CPUs).
void * AlignedAlloc(size_t alignment, size_t size)
Allocate memory with the specified alignment.
Definition common.hpp:30

◆ FrameAllocator() [2/3]

helios::memory::FrameAllocator::FrameAllocator ( const FrameAllocator )
delete

◆ FrameAllocator() [3/3]

helios::memory::FrameAllocator::FrameAllocator ( FrameAllocator &&  other)
inlinenoexcept

Definition at line 194 of file frame_allocator.hpp.

195 : buffer_(other.buffer_),
196 capacity_(other.capacity_),
197 offset_(other.offset_.load(std::memory_order_acquire)),
198 peak_offset_(other.peak_offset_.load(std::memory_order_acquire)),
199 allocation_count_(other.allocation_count_.load(std::memory_order_acquire)),
200 alignment_waste_(other.alignment_waste_.load(std::memory_order_acquire)) {
201 other.buffer_ = nullptr;
202 other.capacity_ = 0;
203 other.offset_.store(0, std::memory_order_release);
204 other.peak_offset_.store(0, std::memory_order_release);
205 other.allocation_count_.store(0, std::memory_order_release);
206 other.alignment_waste_.store(0, std::memory_order_release);
207}

◆ ~FrameAllocator()

helios::memory::FrameAllocator::~FrameAllocator ( )
inlinenoexcept
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 209 of file frame_allocator.hpp.

209 {
210 if (buffer_ != nullptr) {
211 AlignedFree(buffer_);
212 }
213}
void AlignedFree(void *ptr)
Free memory allocated with AlignedAlloc.
Definition common.hpp:53

Member Function Documentation

◆ Allocate() [1/3]

template<typename T >
T * helios::memory::FrameAllocator::Allocate ( )
inlinenoexcept
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 323 of file frame_allocator.hpp.

323 {
324 constexpr size_t size = sizeof(T);
325 constexpr size_t alignment = std::max(alignof(T), kMinAlignment);
326 auto result = Allocate(size, alignment);
327 return static_cast<T*>(result.ptr);
328}
constexpr size_t kMinAlignment
Minimum alignment for any allocation.

◆ Allocate() [2/3]

template<typename T >
T * helios::memory::FrameAllocator::Allocate ( size_t  count)
inlinenoexcept

Definition at line 331 of file frame_allocator.hpp.

331 {
332 if (count == 0) [[unlikely]] {
333 return nullptr;
334 }
335 constexpr size_t alignment = std::max(alignof(T), kMinAlignment);
336 const size_t size = sizeof(T) * count;
337 auto result = Allocate(size, alignment);
338 return static_cast<T*>(result.ptr);
339}

◆ Allocate() [3/3]

AllocationResult helios::memory::FrameAllocator::Allocate ( size_t  size,
size_t  alignment = kDefaultAlignment 
)
inlinenoexcept

Allocates memory with specified size and alignment.

Warning
Triggers assertion in next cases:
  • Alignment is not a power of 2.
  • Alignment is less than kMinAlignment.
Parameters
sizeNumber of bytes to allocate
alignmentAlignment requirement (must be power of 2)
Returns
AllocationResult with pointer and actual allocated size, or {nullptr, 0} on failure

Definition at line 244 of file frame_allocator.hpp.

244 {
245 HELIOS_ASSERT(IsPowerOfTwo(alignment), "Failed to allocate memory: alignment must be power of 2, got '{}'!",
246 alignment);
247 HELIOS_ASSERT(alignment >= kMinAlignment, "Failed to allocate memory: alignment must be at least '{}', got '{}'!",
248 kMinAlignment, alignment);
249
250 if (size == 0) [[unlikely]] {
251 return {.ptr = nullptr, .allocated_size = 0};
252 }
253
254 // Atomically allocate using compare-and-swap
255 size_t current_offset = offset_.load(std::memory_order_acquire);
256 size_t aligned_offset = 0;
257 size_t new_offset = 0;
258 size_t padding = 0;
259
260 do {
261 // Calculate aligned offset
262 auto* current_ptr = static_cast<uint8_t*>(buffer_) + current_offset;
263 padding = CalculatePadding(current_ptr, alignment);
264 aligned_offset = current_offset + padding;
265
266 // Check if we have enough space
267 if (aligned_offset + size > capacity_) {
268 return {.ptr = nullptr, .allocated_size = 0};
269 }
270
271 new_offset = aligned_offset + size;
272
273 // Try to atomically update offset
274 } while (
275 !offset_.compare_exchange_weak(current_offset, new_offset, std::memory_order_release, std::memory_order_acquire));
276
277 // Update stats (these don't need to be perfectly accurate)
278 allocation_count_.fetch_add(1, std::memory_order_relaxed);
279 alignment_waste_.fetch_add(padding, std::memory_order_relaxed);
280
281 // Update peak offset
282 size_t current_peak = peak_offset_.load(std::memory_order_acquire);
283 while (new_offset > current_peak) {
284 if (peak_offset_.compare_exchange_weak(current_peak, new_offset, std::memory_order_release,
285 std::memory_order_acquire)) {
286 break;
287 }
288 }
289
290 void* result = static_cast<uint8_t*>(buffer_) + aligned_offset;
291 return {.ptr = result, .allocated_size = size};
292}
size_t CalculatePadding(const void *ptr, size_t alignment) noexcept
Calculate padding needed for alignment.
constexpr bool IsPowerOfTwo(size_t size) noexcept
Helper function to check if a size is a power of 2.

◆ AllocateAndConstruct()

template<typename T , typename... Args>
requires std::constructible_from<T, Args...>
T * helios::memory::FrameAllocator::AllocateAndConstruct ( Args &&...  args)
inlinenoexcept
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 343 of file frame_allocator.hpp.

343 {
344 T* ptr = Allocate<T>();
345 if (ptr != nullptr) [[likely]] {
346 std::construct_at(ptr, std::forward<Args>(args)...);
347 }
348 return ptr;
349}

◆ AllocateAndConstructArray()

template<typename T >
requires std::default_initializable<T>
T * helios::memory::FrameAllocator::AllocateAndConstructArray ( size_t  count)
inlinenoexcept
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 353 of file frame_allocator.hpp.

353 {
354 T* ptr = Allocate<T>(count);
355 if (ptr != nullptr) [[likely]] {
356 for (size_t i = 0; i < count; ++i) {
357 std::construct_at(ptr + i);
358 }
359 }
360 return ptr;
361}

◆ Capacity()

size_t helios::memory::FrameAllocator::Capacity ( ) const
inlinenoexcept

◆ CurrentOffset()

size_t helios::memory::FrameAllocator::CurrentOffset ( ) const
inlinenoexcept

Gets the current offset (amount of memory used).

Returns
Current offset in bytes
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 169 of file frame_allocator.hpp.

169{ return offset_.load(std::memory_order_relaxed); }

◆ Empty()

bool helios::memory::FrameAllocator::Empty ( ) const
inlinenoexcept

Checks if the allocator is empty.

Returns
True if no allocations have been made since last reset
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/allocator_resources.hpp, and /home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 145 of file frame_allocator.hpp.

145{ return offset_.load(std::memory_order_relaxed) == 0; }

◆ FreeSpace()

size_t helios::memory::FrameAllocator::FreeSpace ( ) const
inlinenoexcept

Gets the amount of free space remaining.

Returns
Free space in bytes
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 317 of file frame_allocator.hpp.

317 {
318 const size_t current = offset_.load(std::memory_order_relaxed);
319 return current < capacity_ ? capacity_ - current : 0;
320}

◆ Full()

bool helios::memory::FrameAllocator::Full ( ) const
inlinenoexcept

Checks if the allocator is full.

Returns
True if no more allocations can be made without reset
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 151 of file frame_allocator.hpp.

151{ return offset_.load(std::memory_order_relaxed) >= capacity_; }

◆ operator=() [1/2]

◆ operator=() [2/2]

FrameAllocator & helios::memory::FrameAllocator::operator= ( FrameAllocator &&  other)
inlinenoexcept

Definition at line 215 of file frame_allocator.hpp.

215 {
216 if (this == &other) [[unlikely]] {
217 return *this;
218 }
219
220 // Free current buffer
221 if (buffer_ != nullptr) {
222 AlignedFree(buffer_);
223 }
224
225 // Move from other
226 buffer_ = other.buffer_;
227 capacity_ = other.capacity_;
228 offset_.store(other.offset_.load(std::memory_order_acquire), std::memory_order_release);
229 peak_offset_.store(other.peak_offset_.load(std::memory_order_acquire), std::memory_order_release);
230 allocation_count_.store(other.allocation_count_.load(std::memory_order_acquire), std::memory_order_release);
231 alignment_waste_.store(other.alignment_waste_.load(std::memory_order_acquire), std::memory_order_release);
232
233 // Reset other
234 other.buffer_ = nullptr;
235 other.capacity_ = 0;
236 other.offset_.store(0, std::memory_order_release);
237 other.peak_offset_.store(0, std::memory_order_release);
238 other.allocation_count_.store(0, std::memory_order_release);
239 other.alignment_waste_.store(0, std::memory_order_release);
240
241 return *this;
242}

◆ Reset()

void helios::memory::FrameAllocator::Reset ( )
inlinenoexcept

Resets the allocator, freeing all allocations.

Resets the internal offset to 0, effectively freeing all memory. Does not actually free or zero the underlying buffer.

Warning
All pointers obtained from this allocator become invalid after this call. Do not store references or pointers to frame-allocated data beyond the current frame.
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/allocator_resources.hpp, and /home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 294 of file frame_allocator.hpp.

294 {
295 offset_.store(0, std::memory_order_release);
296 alignment_waste_.store(0, std::memory_order_release);
297 allocation_count_.store(0, std::memory_order_release);
298}

◆ Stats()

AllocatorStats helios::memory::FrameAllocator::Stats ( ) const
inlinenoexcept

Gets current allocator statistics.

Returns
AllocatorStats with current usage information
Examples
/home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/allocator_resources.hpp, and /home/runner/work/HeliosEngine/HeliosEngine/src/core/include/helios/core/memory/frame_allocator.hpp.

Definition at line 300 of file frame_allocator.hpp.

300 {
301 const size_t current_offset = offset_.load(std::memory_order_relaxed);
302 const size_t peak = peak_offset_.load(std::memory_order_relaxed);
303 const size_t alloc_count = allocation_count_.load(std::memory_order_relaxed);
304 const size_t waste = alignment_waste_.load(std::memory_order_relaxed);
305
306 return {
307 .total_allocated = current_offset,
308 .total_freed = 0,
309 .peak_usage = peak,
310 .allocation_count = alloc_count,
311 .total_allocations = alloc_count,
312 .total_deallocations = 0,
313 .alignment_waste = waste,
314 };
315}