Making Saves Cheap
Aegis Save is architected so that games which tell it what changed pay only for what changed. This guide explains how change detection works, why the default configuration is conservative, and how to use state revision counters to achieve sub-millisecond autosaves across 10,000+ actors.
1. The Dirty Fraction: Why Aegis Won't Guess
The single largest cost factor in any save system is serializing property reflection trees:
- In a world with 10,000 persistent actors, if only 20 actors changed during the last 60 seconds of gameplay, serializing all 10,000 actors is a colossal waste of CPU.
- However, Aegis will not guess whether an actor changed by hashing its properties in memory: serializing an actor to see if its bytes changed costs the exact serialization price you were trying to avoid!
- Furthermore, guessing incorrectly in the cheap direction means a player loses progress.
Mandatory Disclosure (FO-15)
By default, Aegis Save operates in Automatic mode: it conservatively inspects every persistent actor on each save pass to guarantee player data is never lost. Making saves scale with what changed is something your game signals to Aegis, not a project settings slider.
2. The Cheap Signal: GetAegisStateRevision
The fastest and most elegant way to tell Aegis an actor is clean is by implementing IAegisPersistenceEvents::GetAegisStateRevision.
GetAegisStateRevision is a lightweight BlueprintNativeEvent:
- Aegis invokes it on the actor during the capture walk.
- It returns an integer (e.g.
int32 RevisionCounter). - Aegis compares this integer against the revision it recorded at the last save.
- If the revision matches, Aegis completely skips the entire property reflection walk!
C++ Implementation
Implement the interface on your actor:
#include "Identity/AegisPersistenceEvents.h"
UCLASS()
class AMyLootChest : public AActor, public IAegisPersistenceEvents
{
GENERATED_BODY()
public:
// Simply return your internal mutation revision counter
virtual int32 GetAegisStateRevision_Implementation() const override
{
return ChestRevision;
}
void ModifyChestContents()
{
// Whenever state changes, bump the counter!
ChestRevision++;
}
private:
UPROPERTY(SaveGame)
int32 ChestRevision = 0;
};Blueprint Implementation
- In your Actor Blueprint, click Class Settings in the toolbar.
- In the Details panel under Interfaces, click Add and choose
Aegis Persistence Events. - In the My Blueprint panel under Interfaces, double-click Get Aegis State Revision.
- Wire your integer variable (e.g.,
CurrentRevision) to the Return Value pin.
3. The Explicit Signal: MarkChanged
If you do not want to maintain a revision counter, you can manually flag an actor as dirty whenever it mutates:
[Mark Changed (Aegis)]
└── Target: (Self)#include "Core/AegisSubsystem.h"
UAegisSubsystem* Aegis = UAegisSubsystem::Get(this);
if (Aegis)
{
Aegis->MarkChanged(this);
}Calling MarkChanged immediately flags the actor as dirty, guaranteeing that it will be captured on the next save pass regardless of mode.
4. Change Detection Modes
On UAegisPersistenceComponent, you can set the Change Detection property:
| Mode | Property Walk is Skipped When | Fallback Behavior |
|---|---|---|
Automatic (Default) | Object implements GetAegisStateRevision and revision is unchanged. | Walks the actor. Guaranteed 100% correct, conservative default. |
Revision | Revision counter is unchanged. | Logs a warning if interface is missing and treats as Always. |
Manual | MarkChanged has not been called since the last save pass. | You take full responsibility for signaling changes. |
Always | Never skipped. Actor is captured on every save. | Use for actors that mutate continuously every frame. |
5. Performance Impact at Scale
Here is the real-world measured impact across a world with 10,000 persistent actors:
| Strategy | Actors Walked per Save | Added Frame Time (p99) | CPU Overhead |
|---|---|---|---|
| Unoptimized (Full Walk) | 10,000 actors | ~4.0 ms | High |
Optimized (GetAegisStateRevision) | ~200 actors (2% dirty) | 0.60 ms | Negligible (< 1.0 ms) |
By supplying revision counters on your placed actors, you turn a multi-millisecond full world walk into a sub-millisecond incremental update!
Next Steps
- Understand frame budgets in The Per-Frame Budget.
- See measured standalone benchmarks in Benchmarks & Reproduction.