perf(render): bound animation and alpha scratch residency
Complete Slice D3 by replacing the unbounded animation dictionary with a concurrent byte/count LRU and by putting the three retail alpha scratch owners behind one typed aggregate budget. Preserve immediate growth and draw order while reclaiming one-frame density spikes after sustained under-use. Close stale bounds-cache issue evidence without inventing a cache.
This commit is contained in:
parent
3e18fc2730
commit
f2644d42c2
18 changed files with 857 additions and 34 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using AcDream.App.Audio;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Residency;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.Spells;
|
||||
using AcDream.Content;
|
||||
|
|
@ -43,6 +44,7 @@ internal sealed record ContentEffectsAudioResult(
|
|||
internal sealed record ContentEffectsAudioDependencies(
|
||||
string DatDirectory,
|
||||
string PreparedAssetPath,
|
||||
ResidencyBudgetOptions ResidencyBudgets,
|
||||
PhysicsDataCache PhysicsDataCache,
|
||||
bool DumpMotionEnabled,
|
||||
Spellbook SpellBook,
|
||||
|
|
@ -84,7 +86,10 @@ internal interface IContentEffectsAudioCompositionFactory
|
|||
MagicCatalog LoadMagicCatalog(IDatReaderWriter dats);
|
||||
void InstallSpellMetadata(Spellbook spellBook, MagicCatalog catalog);
|
||||
int GetSpellCount(MagicCatalog catalog);
|
||||
IAnimationLoader CreateAnimationLoader(IDatReaderWriter dats);
|
||||
IAnimationLoader CreateAnimationLoader(
|
||||
IDatReaderWriter dats,
|
||||
long maximumEstimatedBytes,
|
||||
int maximumEntries);
|
||||
LiveEntityCollisionBuilder CreateCollisionBuilder(
|
||||
PhysicsDataCache physicsData,
|
||||
IDatReaderWriter dats,
|
||||
|
|
@ -137,8 +142,14 @@ internal sealed class RetailContentEffectsAudioCompositionFactory
|
|||
|
||||
public int GetSpellCount(MagicCatalog catalog) => catalog.SpellTable.Count;
|
||||
|
||||
public IAnimationLoader CreateAnimationLoader(IDatReaderWriter dats) =>
|
||||
new RetailAnimationLoader(dats);
|
||||
public IAnimationLoader CreateAnimationLoader(
|
||||
IDatReaderWriter dats,
|
||||
long maximumEstimatedBytes,
|
||||
int maximumEntries) =>
|
||||
new RetailAnimationLoader(
|
||||
dats,
|
||||
maximumEstimatedBytes,
|
||||
maximumEntries);
|
||||
|
||||
public LiveEntityCollisionBuilder CreateCollisionBuilder(
|
||||
PhysicsDataCache physicsData,
|
||||
|
|
@ -302,7 +313,10 @@ internal sealed class ContentEffectsAudioCompositionPhase :
|
|||
$"spells: loaded {_factory.GetSpellCount(magic)} entries from portal.dat");
|
||||
Fault(ContentEffectsAudioCompositionPoint.SpellMetadataInstalled);
|
||||
|
||||
IAnimationLoader animations = _factory.CreateAnimationLoader(dats);
|
||||
IAnimationLoader animations = _factory.CreateAnimationLoader(
|
||||
dats,
|
||||
_dependencies.ResidencyBudgets.AnimationBytes,
|
||||
_dependencies.ResidencyBudgets.AnimationEntries);
|
||||
_publication.PublishAnimationLoader(animations);
|
||||
Fault(ContentEffectsAudioCompositionPoint.AnimationLoaderPublished);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using AcDream.App.Input;
|
|||
using AcDream.App.Interaction;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Residency;
|
||||
using AcDream.App.Rendering.Selection;
|
||||
using AcDream.App.Rendering.Sky;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
|
|
@ -586,6 +587,9 @@ internal sealed class LivePresentationCompositionPhase
|
|||
{
|
||||
LivePresentationDependencies d = _dependencies;
|
||||
WorldRenderFoundation foundation = world.Foundation;
|
||||
AlphaScratchBudgetProfile alphaScratchBudgets =
|
||||
AlphaScratchBudgetProfile.Create(
|
||||
d.Options.ResidencyBudgets.AlphaScratchBytes);
|
||||
|
||||
var selectionScene = new RetailSelectionScene(
|
||||
new RetailSelectionGeometryCache(content.Dats, d.DatLock));
|
||||
|
|
@ -601,7 +605,8 @@ internal sealed class LivePresentationCompositionPhase
|
|||
d.ClassificationCache,
|
||||
d.TranslucencyFades,
|
||||
selectionScene,
|
||||
d.RetailAlphaQueue),
|
||||
d.RetailAlphaQueue,
|
||||
alphaScratchBudgets.DispatcherBytes),
|
||||
static value => value.Dispose());
|
||||
var selectionQuery = new WorldSelectionQuery(
|
||||
liveEntities,
|
||||
|
|
@ -883,7 +888,8 @@ internal sealed class LivePresentationCompositionPhase
|
|||
foundation.TextureCache,
|
||||
content.Dats,
|
||||
foundation.MeshAdapter,
|
||||
d.RetailAlphaQueue),
|
||||
d.RetailAlphaQueue,
|
||||
alphaScratchBudgets.ParticleBytes),
|
||||
static value => value.Dispose());
|
||||
Fault(LivePresentationCompositionPoint.SkyAndParticlesCreated);
|
||||
|
||||
|
|
@ -966,6 +972,19 @@ internal sealed class LivePresentationCompositionPhase
|
|||
frameDiagnostics,
|
||||
bindings,
|
||||
landblockLoaded);
|
||||
foundation.Residency.RegisterDomainSource(
|
||||
new DelegateResidencyDomainSource(
|
||||
ResidencyDomain.AlphaScratch,
|
||||
() => new ResidencyDomainSnapshot(
|
||||
ResidencyDomain.AlphaScratch,
|
||||
EntryCount: 3,
|
||||
OwnerCount: 3,
|
||||
Charges: new ResidencyCharges(
|
||||
ScratchBytes: checked(
|
||||
d.RetailAlphaQueue.RetainedScratchBytes
|
||||
+ dispatcherLease.Resource.RetainedAlphaScratchBytes
|
||||
+ particleLease.Resource.RetainedAlphaScratchBytes)),
|
||||
BudgetBytes: alphaScratchBudgets.TotalBytes)));
|
||||
_publication.PublishLivePresentation(result);
|
||||
|
||||
liveRuntimeLease.Transfer();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ using AcDream.App.Rendering.Wb;
|
|||
using AcDream.App.Rendering.Residency;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Content;
|
||||
using AcDream.Content.Vfx;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Terrain;
|
||||
using AcDream.UI.Abstractions.Settings;
|
||||
using DatReaderWriter;
|
||||
|
|
@ -116,7 +118,8 @@ internal interface IWorldRenderCompositionFactory
|
|||
ResidencyManager manager,
|
||||
WbMeshAdapter meshes,
|
||||
TextureCache textures,
|
||||
IPreparedAssetSource preparedAssets);
|
||||
IPreparedAssetSource preparedAssets,
|
||||
IAnimationLoader animations);
|
||||
SamplerCache CreateSamplerCache(GL gl);
|
||||
void Release(IDisposable resource);
|
||||
}
|
||||
|
|
@ -295,7 +298,8 @@ internal sealed class RetailWorldRenderCompositionFactory
|
|||
ResidencyManager manager,
|
||||
WbMeshAdapter meshes,
|
||||
TextureCache textures,
|
||||
IPreparedAssetSource preparedAssets)
|
||||
IPreparedAssetSource preparedAssets,
|
||||
IAnimationLoader animations)
|
||||
{
|
||||
meshes.RegisterResidencySources(manager);
|
||||
textures.RegisterResidencySources(manager);
|
||||
|
|
@ -308,6 +312,25 @@ internal sealed class RetailWorldRenderCompositionFactory
|
|||
Charges: new ResidencyCharges(
|
||||
MappedVirtualBytes:
|
||||
preparedAssets.MappedVirtualBytes))));
|
||||
if (animations is RetailAnimationLoader retailAnimations)
|
||||
{
|
||||
manager.RegisterDomainSource(new DelegateResidencyDomainSource(
|
||||
ResidencyDomain.Animations,
|
||||
() =>
|
||||
{
|
||||
AnimationCacheDiagnostics diagnostics =
|
||||
retailAnimations.Diagnostics;
|
||||
return new ResidencyDomainSnapshot(
|
||||
ResidencyDomain.Animations,
|
||||
EntryCount: diagnostics.Count,
|
||||
OwnerCount: 0,
|
||||
Charges: new ResidencyCharges(
|
||||
DecodedBytes:
|
||||
diagnostics.EstimatedBytes),
|
||||
BudgetBytes: diagnostics.BudgetBytes,
|
||||
Evictions: diagnostics.Stats.Evictions);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public SamplerCache CreateSamplerCache(GL gl) => new(gl);
|
||||
|
|
@ -493,7 +516,8 @@ internal sealed class WorldRenderCompositionPhase
|
|||
residency,
|
||||
meshAdapter,
|
||||
textureCache,
|
||||
content.PreparedAssets);
|
||||
content.PreparedAssets,
|
||||
content.AnimationLoader);
|
||||
|
||||
scope.Complete();
|
||||
_dependencies.Log(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue