Ownership and Lifetimes
Memory Management Model
VkEngine uses a hierarchical ownership model with deferred destruction:
- Engine Ownership: Engine singleton owns scenes, resources, and allocator
- Scene Ownership: Active scene owns its game objects, physics actors, and per-scene resources
- Object Ownership: Each GameObject owns its Vulkan/PhysX resources
- Deferred Deletion: Objects are queued for destruction and cleaned up on the next safe point
Game Object Lifetime
Creation
Game objects are created within scenes using a template factory:
// In Scene::InitScene(Engine* engine)
Mesh* mesh = engine->getMesh("player_mesh");
Texture* texture = engine->getTexture("player_texture");
PhysicsMaterial* material = engine->createPhysicsMaterial(0.5f, 0.3f, 0.2f);
auto player = engine->createGameObject<PlayerCharacter>(
transform, // Initial position, rotation, scale
mesh, // Render geometry
texture, // Surface appearance
material, // Physics properties
true // isDynamic - false for static colliders
);
// Engine calls player->Start() automatically
Lifecycle Phases
- Construction: Object memory is allocated with custom allocator, constructor runs
- Engine Integration: Engine stores object metadata (ID, physics actor, audio group)
- Start:
GameObject::Start(Engine*)called - user initialization - Active: Object participates in physics, rendering, and updates each frame
- Update:
GameObject::Update(Engine*)called every frame - Destruction: Deferred via
engine->requestDestroyGameObject(object)or scene unload
Destruction Pattern
Objects are never destroyed immediately. Instead:
void MyScene::UpdateScene(Engine* engine) {
if (shouldRemoveObject) {
engine->requestDestroyGameObject(object);
// object still valid here
}
}
// Later, engine checks queues and calls:
// - object->Destroy(engine)
// - PhysX actor cleanup
// - Audio channel cleanup
// - Memory deallocation
This prevents iterator invalidation and double-deletion bugs during game loop execution.
Resource Lifetime
Creation and Caching
Resources are created and cached globally by the engine:
// First call: loads from disk, caches result
Mesh* mesh = engine->createMesh("player_mesh", "assets/player.obj");
// Subsequent calls: returns cached instance
Mesh* sameMesh = engine->getMesh("player_mesh");
Per-Scene Resources
The active scene maintains collections of available resources:
sceneMeshes- Meshes used by objects in this scenesceneTextures- Textures used by objects in this scenesceneGameObjects- All instantiated objects
Cleanup on Scene Transition
When a new scene is loaded:
engine->loadScene(nextScene); // Implicit unload of current scene
This triggers:
Scene::DestroyScene()for active scene- Destruction of all game objects in active scene
- Cleanup of scene-specific resources (through resource destruction queue)
- Initialization of new scene:
EarlyInitScene()thenInitScene()
Vulkan Resource Management
Buffers and Images
Each GPU resource (vertex buffer, index buffer, image) is owned by its containing object:
- GameObject Rendering: Owns mesh buffers and texture images
- Descriptor Sets: Allocated from global descriptor pool, per-frame recycling
- Frame Buffering: Uses
MAX_FRAMES_IN_FLIGHT = 2with dual-buffered command buffers and synchronization primitives - Synchronization: Fences and semaphores prevent CPU-GPU synchronization hazards
Swapchain Management
- Created during
Engine::init() - Recreated on window resize via framebuffer callback
- Images owned by GLFW/Vulkan driver, not VkEngine
PhysX Resource Management
Physics Actors
Each GameObject with physics owns a PxRigidActor (PxRigidStatic or PxRigidDynamic):
// Created during engine->createGameObject<>() with mesh and material
// Automatically removed when object is destroyed
// Shape and material owned by PhysX internally
Character Controllers
Created separately from GameObjects:
ICharacterController* controller = engine->createCharacterController(
height, radius, position,
material,
interactWithActors // Whether to interact with dynamic objects
);
// Request destruction separately
engine->requestDestroyCharacterController(controller);
Triggers
Physics-only entities without rendering:
Trigger* trigger = engine->createBoxTrigger(position, size);
trigger->onTriggerEnter = [](GameObject* other) { /* ... */ };
trigger->onTriggerExit = [](GameObject* other) { /* ... */ };
// Request destruction when done
engine->requestDestroyTrigger(trigger);
Audio Resource Management
Sounds
Created by engine and cached globally:
Sound* sfx = engine->createSound("footstep", "assets/step.ogg", false, true);
// false = not looping
// true = 3D spatial audio
Channel Groups
Each GameObject has a private FMOD::ChannelGroup* for sound isolation:
- Owned by engine’s FMOD system
- Destroyed when GameObject is destroyed
- Used for per-object volume and pause control
Spatial Audio
Position and velocity updated automatically from GameObject transform each frame.
Memory Allocation Strategy
Custom Allocator
The engine uses a tracking allocator (if enabled):
void* Engine::requestMemory(size_t size);
void Engine::freeMemory(void* ptr);
With object header pattern for type-safe destruction:
struct ObjectHeader {
void (*destroy)(void*); // Function pointer to typed destructor
void* allocationBase; // Pointer to raw allocation
};
Developers using engine APIs don’t need to manage this directly - it’s handled internally.
Container Allocators
STL containers in scenes use EngineAllocator<T>:
- Simple malloc/free wrapper
- Allows optional memory tracking for debugging
- Applied to scene resource collections
Lifetime Rules Summary
| Resource | Created By | Owned By | Destroyed By | When |
|---|---|---|---|---|
| GameObject | engine->createGameObject<>() | Engine | requestDestroyGameObject() or scene unload | Next frame |
| Mesh | engine->createMesh() | Engine (global cache) | Manual via requestDestroy() or engine cleanup | On request or exit |
| Texture | engine->createTexture() | Engine (global cache) | Manual via requestDestroy() or engine cleanup | On request or exit |
| Sound | engine->createSound() | Engine (global cache) | Manual via requestDestroy() or engine cleanup | On request or exit |
| Scene | engine->createScene<>() | Engine | requestDestroyScene() or manual | On request or exit |
| Trigger | engine->createBoxTrigger() | Engine | requestDestroyTrigger() | On request or exit |
| CharacterController | engine->createCharacterController() | Engine | requestDestroyCharacterController() | On request or exit |
| PhysicsMaterial | engine->createPhysicsMaterial() | Engine | Manual via requestDestroy() or engine cleanup | On request or exit |