using Ghost.Core; using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using System.Diagnostics.CodeAnalysis; namespace Ghost.Graphics.RenderGraphModule; /// /// 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); // Placed resource metadata public readonly List placedResources = new(32); // Compiled barriers (stores only target states, queries before state from ResourceDatabase) public readonly List compiledBarriers = new(128); // Real gpu resource public readonly List> backingResources = new(32); // View state used for this compilation public ViewState viewState; public void Clear() { compiledPassIndices.Clear(); passCulledFlags.Clear(); logicalToPhysical.Clear(); placedResources.Clear(); compiledBarriers.Clear(); backingResources.Clear(); viewState = default; } } /// /// Placed resource data for caching. /// internal struct PlacedResourceData { public int index; public RenderGraphResourceType type; public ulong heapOffset; public ulong sizeInBytes; 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 readonly CachedCompilation _cached = new(); private ulong _cachedHash; 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.placedResources.AddRange(data.placedResources); _cached.compiledBarriers.AddRange(data.compiledBarriers); _cached.backingResources.AddRange(data.backingResources); } /// /// Invalidates the cache, forcing recompilation on next Compile(). /// public void Invalidate() { _hasCachedData = false; _cachedHash = 0; _cached.Clear(); } public void UpdateBackingResource(int logicalIndex, Handle resource) { if (logicalIndex < 0 || logicalIndex >= _cached.backingResources.Count) { return; } _cached.backingResources[logicalIndex] = resource; } /// /// 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); } }