using System.Diagnostics.CodeAnalysis; namespace Ghost.RenderGraph.Concept; /// /// Represents cached compilation results for a render graph. /// This avoids recompiling the graph when the structure hasn't changed. /// internal sealed class CachedCompilation { // Compiled pass indices (indices into the _passes list) public readonly List compiledPassIndices = new(64); // Culling decisions for each pass public readonly List passCulledFlags = new(64); // Physical resource aliasing mappings (logical index -> physical index) public readonly Dictionary logicalToPhysical = new(128); // Physical resource metadata public readonly List physicalResources = new(32); // Resource barriers public readonly List barriers = new(128); // Resource state mappings (for barrier generation) public readonly Dictionary resourceStates = new(128); public void Clear() { compiledPassIndices.Clear(); passCulledFlags.Clear(); logicalToPhysical.Clear(); physicalResources.Clear(); barriers.Clear(); resourceStates.Clear(); } } /// /// Physical resource data for caching. /// internal struct PhysicalResourceData { public int index; public int width; public int height; public TextureFormat format; public int firstUsePass; public int lastUsePass; } /// /// Manages compilation caching for render graphs. /// Stores compiled results and allows cache hits when graph structure is unchanged. /// internal sealed class RenderGraphCompilationCache { private ulong _cachedHash; private readonly CachedCompilation _cached = new(); private bool _hasCachedData; // Statistics public int CacheHits { get; private set; } public int CacheMisses { get; private set; } /// /// Attempts to retrieve cached compilation results. /// public bool TryGetCached(ulong hash, [MaybeNullWhen(false)] out CachedCompilation result) { if (_hasCachedData && _cachedHash == hash) { result = _cached; CacheHits++; return true; } result = null; CacheMisses++; return false; } /// /// Stores compilation results in the cache. /// public void Store(ulong hash, CachedCompilation data) { _cachedHash = hash; _hasCachedData = true; // Deep copy the data _cached.Clear(); _cached.compiledPassIndices.AddRange(data.compiledPassIndices); _cached.passCulledFlags.AddRange(data.passCulledFlags); foreach (var kvp in data.logicalToPhysical) { _cached.logicalToPhysical[kvp.Key] = kvp.Value; } _cached.physicalResources.AddRange(data.physicalResources); _cached.barriers.AddRange(data.barriers); foreach (var kvp in data.resourceStates) { _cached.resourceStates[kvp.Key] = kvp.Value; } } /// /// Invalidates the cache, forcing recompilation on next Compile(). /// public void Invalidate() { _hasCachedData = false; _cachedHash = 0; _cached.Clear(); } /// /// Gets cache statistics for debugging. /// public (int hits, int misses, double hitRate) GetStatistics() { int total = CacheHits + CacheMisses; double hitRate = total > 0 ? (double)CacheHits / total : 0.0; return (CacheHits, CacheMisses, hitRate); } }