Upgraded MemoryArena to allow poping memory and creating an arena for a specific part and not pushing completely on the same stack.
Made with Gemini + Updates skills to have better agents.
This commit is contained in:
@@ -1,45 +1,64 @@
|
||||
# Memory System Status & Migration Plan
|
||||
# Paged Memory Arena Architecture
|
||||
|
||||
## Completed Work
|
||||
## Status
|
||||
**Implemented & Active** (Jan 2026)
|
||||
|
||||
### Core Systems
|
||||
- **MemoryArena**: Implemented linear allocator with alignment, markers, and reset support.
|
||||
- **Global Arenas**:
|
||||
- `ScratchArena` (64MB): Transient per-frame memory.
|
||||
- `EngineArena` (256MB): Persistent engine memory (internal).
|
||||
- `GameArena` (512MB): Persistent game memory (exported to Game DLL).
|
||||
- **Helpers**: Added `ArenaPushType<T>` and `ArenaPushArray<T>` with automatic zero-initialization.
|
||||
## Overview
|
||||
The memory system uses a **Paged Arena** model backed by global **Memory Pools**. This architecture supports indefinite growth, efficient clearing, and minimizes OS-level allocations by sub-allocating from large pre-reserved buffers.
|
||||
|
||||
### Migrated Subsystems
|
||||
- **Display**: `Win32Window` and `Win32DisplayDevice` now use `EngineArena`.
|
||||
- **Game Entities**: `Entity.h` uses `GameArena` for entity allocation; manual `free` calls removed from `game.cpp`.
|
||||
- **Hot Reload**: `HotReload.cpp` (persistent paths to `EngineArena`) and `Win32HotReload.cpp` (temp paths to `ScratchArena`).
|
||||
## Architecture
|
||||
|
||||
## Remaining Work
|
||||
### 1. Memory Pool (`MemoryPool`)
|
||||
A Global Source of memory blocks.
|
||||
- **Role**: Manages a large contiguous memory region (e.g., 512MB for Game).
|
||||
- **Implementation**: Free List Allocator.
|
||||
- **Operations**: `AllocateBlock` (First-Fit with Splitting), `FreeBlock` (Returns to list).
|
||||
- **Optimization**: **Block Splitting** is implemented to preserve free space. If a block in the free list is significantly larger than requested (`Alloc + Header + 16 bytes`), it is split, and the remainder is returned to the free list. This prevents pool exhaustion from small allocations consuming large blocks.
|
||||
- **Instances**:
|
||||
- `g_EngineMemory` (256MB)
|
||||
- `g_GameMemory` (512MB)
|
||||
- `g_ScratchMemory` (64MB)
|
||||
|
||||
The following subsystems still use legacy `malloc`/`calloc`/`realloc`/`free` and need to be migrated.
|
||||
### 2. Memory Arena (`MemoryArena`)
|
||||
A High-Level Allocator.
|
||||
- **Structure**: A linked list of `MemoryBlocks`.
|
||||
- **Behavior**:
|
||||
- **Growth**: Starts with one block. If an allocation exceeds capacity, it requests a new Block (Page) from the backing Pool and links it.
|
||||
- **Alloc**: Linear bump-pointer within the current block.
|
||||
- **Clear**: Returns all blocks (except the first) to the Pool. Resets the first block.
|
||||
- **Realloc**: Supports in-place expansion (if top of stack) or copy-and-move.
|
||||
- **Instances**: `GetGameArena()`, `GetEngineArena()`, `GetScratchArena()`.
|
||||
|
||||
### 3. Memory Block (`MemoryBlock`)
|
||||
The unit of exchange between Pool and Arena.
|
||||
- **Header**: Includes `Magic` (debug safety), `Next` pointer, `TotalSize` (renamed from `Size`), and `Used` offset.
|
||||
- **Alignment**: 16-byte alignment enforced.
|
||||
- **Safety**: Debug builds use memory poisoning (`0xCD` on alloc, `0xDD` on free) and Magic number checks to detect corruption.
|
||||
|
||||
### 4. Arena Pop (`ArenaPop`)
|
||||
Support for LIFO allocations (reclaiming memory).
|
||||
- **Behavior**: Checks if the pointer is at the very top of the stack (`CurrentBlock->Used`).
|
||||
- **Optimization**: If valid, decrements `Used` to reclaim space. If not (fragmented), does nothing.
|
||||
- **Usage**: Critical for `ImGui` vector resizing to prevent exponential memory consumption.
|
||||
|
||||
### IO System
|
||||
- **Files**: `Core/HAL/IO/IOStream.cpp`, `Core/HAL/IO/Win32/Win32IOStream.cpp`
|
||||
- **Allocations**: `IOStream` instance, data buffers, `Win32IOStreamDataPayload`.
|
||||
- **Challenge**: `Realloc` is used for growing buffers.
|
||||
- **Strategy**:
|
||||
- `IOStream` struct -> `ScratchArena` (if transient) or `EngineArena`.
|
||||
- Buffers: Evaluate if `ArenaPush` with large enough capacity is sufficient, or implement a growable buffer on top of arena (or use `std::vector` with custom allocator if absolutely needed, but prefer simple fixed max size if possible).
|
||||
## Usage
|
||||
|
||||
### Graphics / Debug
|
||||
- **Files**: `Graphics/DebugDisplayRenderer.cpp`
|
||||
- **Allocations**: `DepthTestedVertices`, `OverlayVertices`.
|
||||
- **Strategy**: Use `EngineArena` or a dedicated `RenderArena` if these are persistent. If per-frame, move to `ScratchArena`.
|
||||
```cpp
|
||||
// 1. Get an Arena
|
||||
MemoryArena* arena = GetScratchArena();
|
||||
|
||||
### Shader Compiler
|
||||
- **Files**: `JulietShaderCompiler/ShaderCompiler.cpp`
|
||||
- **Allocations**: Argument arrays, file buffers.
|
||||
- **Strategy**: Use `ScratchArena` for all compilation tasks as they are transient.
|
||||
// 2. Push Data
|
||||
MyStruct* data = ArenaPushType<MyStruct>(arena, "Tag");
|
||||
void* raw = ArenaPush(arena, 1024, 16, "RawBuffer");
|
||||
|
||||
### Filesystem
|
||||
- **Files**: `Core/HAL/Filesystem/Filesystem.cpp`
|
||||
- **Allocations**: `CachedBasePath`.
|
||||
- **Strategy**: Migrate to `EngineArena` (persistent).
|
||||
// 3. Pop Data (LIFO)
|
||||
ArenaPop(arena, raw, 1024); // Reclaims memory
|
||||
|
||||
// 4. Reset (Scratch only)
|
||||
ScratchArenaReset(); // Returns pages to g_ScratchMemory
|
||||
```
|
||||
|
||||
## Migration Status
|
||||
- **ImGui**: Migrated to `GetEngineArena()` (Paged) with `ArenaPop` support for efficient vector resizing.
|
||||
- **Display/Window**: Uses Engine Arena.
|
||||
- **Game Entities**: Uses Game Arena.
|
||||
|
||||
Reference in New Issue
Block a user