-
Notifications
You must be signed in to change notification settings - Fork 3
Memory Management Patterns
This page documents the ownership rules, allocation strategies, and debugging tools used throughout SparkEngine.
Source: SparkEngine/Source/Utils/FrameAllocator.h, MemoryDebugger.h, SparkEngine/Source/Graphics/RenderTargetPool.h, GameModules/SparkGame/Source/Projectiles/ProjectilePool.h
SparkEngine follows strict ownership conventions to prevent leaks and dangling pointers:
| Pattern | When to use | Example |
|---|---|---|
std::unique_ptr<T> |
Single owner, transferable | std::unique_ptr<BehaviorTree> m_behaviorTree |
ComPtr<T> |
D3D11/DXGI COM objects | ComPtr<ID3D11Buffer> m_vertexBuffer |
Raw pointer T*
|
Non-owning reference |
Player* m_player (observer, not owner) |
std::shared_ptr<T> |
Shared ownership (rare) |
std::shared_ptr<Asset> in asset cache |
-
No naked
new/delete. All heap allocations use smart pointers or pool allocators. -
unique_ptris the default. Usestd::make_unique<T>()for all single-owner heap objects. -
Raw pointers are non-owning. A raw
T*never implies the holder should delete the object. -
COM objects use
ComPtr. All D3D11 resources (ID3D11Buffer,ID3D11Texture2D, etc.) are wrapped inMicrosoft::WRL::ComPtrfor automaticRelease(). - RAII everywhere. Resources are released in destructors — no manual cleanup calls in normal flow.
Source: SparkEngine/Source/Utils/FrameAllocator.h
A linear (bump) allocator for per-frame temporary allocations. Memory is allocated in O(1) by advancing a pointer and freed in O(1) by resetting the offset to zero.
class FrameAllocator {
public:
explicit FrameAllocator(size_t capacityBytes = 1024 * 1024); // Default 1 MB
void* Allocate(size_t size, size_t alignment = 16);
template<typename T> T* Alloc(size_t count = 1);
template<typename T, typename...Args> T* New(Args&&... args);
void Reset(); // O(1) — resets offset, no destructors called
size_t Used() const;
size_t Remaining() const;
size_t PeakUsed() const;
};// At engine startup
FrameAllocator frameAlloc(2 * 1024 * 1024); // 2 MB
// Each frame
frameAlloc.Reset();
// Allocate temporary data (no free needed)
auto* drawCmds = frameAlloc.Alloc<DrawCommand>(256);
auto* lights = frameAlloc.Alloc<LightData>(maxLights);- Only for trivially destructible types —
Reset()does not call destructors - Allocations are not individually freeable — the entire buffer resets at once
- Thread-unsafe — use one allocator per thread if needed
Source: SparkEngine/Source/Graphics/RenderTargetPool.h
Pools GPU render targets by descriptor (format, dimensions, sample count) to avoid creating and destroying textures every frame.
struct RenderTargetDesc {
uint32_t width, height;
DXGI_FORMAT format;
uint32_t sampleCount, mipLevels;
bool isDepthStencil;
};
class RenderTargetPool {
public:
PooledRTHandle Acquire(const RenderTargetDesc& desc);
void Release(PooledRTHandle handle);
void Tick(); // Reclaim targets idle for N frames
ID3D11RenderTargetView* GetRTV(PooledRTHandle handle);
ID3D11ShaderResourceView* GetSRV(PooledRTHandle handle);
ID3D11Texture2D* GetTexture(PooledRTHandle handle);
RenderTargetPoolMetrics GetMetrics() const;
};// Acquire a target for this frame's bloom pass
auto bloomRT = pool.Acquire({width, height, DXGI_FORMAT_R16G16B16A16_FLOAT, 1, 1, false});
// Render bloom pass using GetRTV(bloomRT)...
// Release back to pool (available for reuse next frame)
pool.Release(bloomRT);
// Each frame, reclaim stale targets
pool.Tick();- Targets idle for 60+ frames are automatically destroyed
- Depth/stencil targets use typeless formats for SRV compatibility
- All internal D3D11 resources use
ComPtrfor RAII
Source: GameModules/SparkGame/Source/Projectiles/ProjectilePool.h
Pre-allocates a fixed number of game objects and recycles them to avoid runtime allocation:
class ProjectilePool {
public:
ProjectilePool(size_t poolSize);
Projectile* GetProjectile(); // O(1) from free list
void ReturnProjectile(Projectile* p); // O(1) back to free list
size_t GetActiveCount() const;
size_t GetAvailableCount() const;
};std::vector<std::unique_ptr<Projectile>> m_projectiles; // Owns all objects
std::queue<Projectile*> m_availableProjectiles; // Free list (non-owning)This pattern applies to any frequently spawned/despawned object: particles, decals, audio sources. The pool owns all objects via unique_ptr; the free queue holds non-owning pointers.
Source: SparkEngine/Source/Utils/MemoryDebugger.h
Debug-build allocation tracker that records every allocation with source location and category, then reports leaks at shutdown.
// Manual tracking
MemoryDebugger::GetInstance().RecordAlloc(ptr, size, "Physics", __FILE__, __LINE__, __func__);
MemoryDebugger::GetInstance().RecordFree(ptr);
// Convenience macros
SPARK_TRACK_ALLOC(ptr, size, "Rendering");
SPARK_TRACK_FREE(ptr);auto leaks = MemoryDebugger::GetInstance().GetLeaks();
for (const auto& leak : leaks) {
// leak.address, leak.size, leak.category, leak.location
}
SPARK_PRINT_LEAK_REPORT(); // Outputs to consoleauto stats = MemoryDebugger::GetInstance().GetCategoryStats();
// stats[i].name, stats[i].currentBytes, stats[i].peakBytes,
// stats[i].totalAllocations, stats[i].totalDeallocations
auto hotspots = MemoryDebugger::GetInstance().GetHotSpots(10);
// Top 10 allocation sites by frequencyAll GPU resources follow this pattern:
class SomeRenderer {
private:
ComPtr<ID3D11Buffer> m_vertexBuffer;
ComPtr<ID3D11Buffer> m_indexBuffer;
ComPtr<ID3D11ShaderResourceView> m_textureSRV;
ComPtr<ID3D11RenderTargetView> m_renderTarget;
};
// ComPtr calls Release() automatically in destructorComPtr<ID3D11Buffer> buffer;
D3D11_BUFFER_DESC desc = {};
desc.ByteWidth = sizeof(Vertex) * vertexCount;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = vertices.data();
HRESULT hr = device->CreateBuffer(&desc, &initData, buffer.GetAddressOf());- Prefer stack allocation for small, short-lived objects
- Use FrameAllocator for per-frame temporary data (draw lists, scratch buffers)
- Use object pools for frequently spawned/despawned objects (projectiles, particles)
- Use RenderTargetPool for GPU render targets that vary by frame
-
Use
unique_ptrfor everything else on the heap -
Use
shared_ptronly when true shared ownership is required (asset cache) - Enable MemoryDebugger in debug builds to catch leaks early
-
Never use raw
new/delete— always wrap in a smart pointer or pool
See Profiler and Debugging for runtime memory profiling tools.
See Memory Safety for type-safe utilities (NonNull, SafeCast, Contracts) and compiler hardening.
Published from 891e00c1cb4a. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers