[Vulkan] Write only the cache shards that gained pipelines
A shard remembers the driver size it was loaded or saved with and is not written again while that size holds. Shard saves are logged as one summary line, and a shard load only when it is slow.
This commit is contained in:
parent
825d72f0fa
commit
cfcebc4cd9
|
|
@ -13,11 +13,14 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
{
|
||||
private sealed class DriverCacheShard
|
||||
{
|
||||
public required Lazy<PipelineCache> Cache;
|
||||
public Lazy<PipelineCache> Cache = null!;
|
||||
public required string Path;
|
||||
public bool Dirty;
|
||||
public nuint SavedBytes;
|
||||
}
|
||||
|
||||
private const double SlowShardLoadMilliseconds = 50;
|
||||
|
||||
private string? _pipelineCacheShardDirectory;
|
||||
private readonly Dictionary<string, DriverCacheShard> _pipelineCacheShards = new();
|
||||
private static readonly bool LegacyShaderGroups =
|
||||
|
|
@ -54,10 +57,10 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
return existing.Cache;
|
||||
}
|
||||
|
||||
var path = Path.Combine(_pipelineCacheShardDirectory, key + ".bin");
|
||||
var source = new Lazy<PipelineCache>(() => LoadGuestPipelineCache(key, path));
|
||||
_pipelineCacheShards.Add(key, new DriverCacheShard { Cache = source, Path = path, Dirty = true });
|
||||
return source;
|
||||
var shard = new DriverCacheShard { Path = Path.Combine(_pipelineCacheShardDirectory, key + ".bin"), Dirty = true };
|
||||
shard.Cache = new Lazy<PipelineCache>(() => LoadGuestPipelineCache(key, shard));
|
||||
_pipelineCacheShards.Add(key, shard);
|
||||
return shard.Cache;
|
||||
}
|
||||
|
||||
private PipelineCache ResolveGuestPipelineCache(Lazy<PipelineCache>? source)
|
||||
|
|
@ -66,8 +69,9 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
return cache.Handle != 0 ? cache : _pipelineCache;
|
||||
}
|
||||
|
||||
private PipelineCache LoadGuestPipelineCache(string key, string path)
|
||||
private PipelineCache LoadGuestPipelineCache(string key, DriverCacheShard shard)
|
||||
{
|
||||
var path = shard.Path;
|
||||
byte[] data = [];
|
||||
try
|
||||
{
|
||||
|
|
@ -90,19 +94,45 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
return default;
|
||||
}
|
||||
|
||||
if (data.Length != 0)
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Vulkan cache shard loaded: key={key} bytes={data.Length} ms={Stopwatch.GetElapsedTime(started).TotalMilliseconds:F1}");
|
||||
if (data.Length == 0)
|
||||
return cache;
|
||||
|
||||
var milliseconds = Stopwatch.GetElapsedTime(started).TotalMilliseconds;
|
||||
if (QueryPipelineCacheSize(cache) is { } size)
|
||||
shard.SavedBytes = size;
|
||||
if (milliseconds >= SlowShardLoadMilliseconds)
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Vulkan cache shard loaded: key={key} bytes={data.Length} ms={milliseconds:F1}");
|
||||
return cache;
|
||||
}
|
||||
|
||||
private void SaveGuestPipelineCaches()
|
||||
{
|
||||
var savedShards = 0;
|
||||
nuint savedBytes = 0;
|
||||
foreach (var shard in _pipelineCacheShards.Values)
|
||||
{
|
||||
if (shard.Dirty && shard.Cache.IsValueCreated && shard.Cache.Value.Handle != 0 &&
|
||||
SaveDriverPipelineCache(shard.Cache.Value, shard.Path, MaxPipelineCacheBytes, out _))
|
||||
if (!shard.Dirty || !shard.Cache.IsValueCreated || shard.Cache.Value.Handle == 0)
|
||||
continue;
|
||||
|
||||
var cache = shard.Cache.Value;
|
||||
if (QueryPipelineCacheSize(cache) is { } size && size == shard.SavedBytes)
|
||||
{
|
||||
shard.Dirty = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SaveDriverPipelineCache(cache, shard.Path, MaxPipelineCacheBytes, out var bytes))
|
||||
{
|
||||
shard.Dirty = false;
|
||||
shard.SavedBytes = bytes;
|
||||
savedShards++;
|
||||
savedBytes += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (savedShards != 0)
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][INFO] Vulkan cache shards saved: directory={_pipelineCacheShardDirectory} shards={savedShards} bytes={savedBytes}");
|
||||
}
|
||||
|
||||
private void DestroyGuestPipelineCaches()
|
||||
|
|
|
|||
|
|
@ -42,6 +42,46 @@ public sealed class VulkanPipelineCacheShardTests(HeadlessVulkanFixture fixture)
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnUnchangedShardIsNotWrittenAgain()
|
||||
{
|
||||
var vulkan = fixture.Vulkan;
|
||||
if (!GatePrerequisites.Ready(vulkan)) return;
|
||||
var directory = Path.Combine(Path.GetTempPath(), "SharpEmuTests", Guid.NewGuid().ToString("N"));
|
||||
using var presenter = new PresenterUnderTest(vulkan);
|
||||
presenter.SetField("_pipelineCacheShardDirectory", directory);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
var path = Path.Combine(directory, "test.bin");
|
||||
_ = ((Lazy<PipelineCache>)presenter.InvokeMethod("GetGuestPipelineCacheSource", "test")!).Value;
|
||||
presenter.InvokeMethod("SaveGuestPipelineCaches");
|
||||
Assert.True(File.Exists(path));
|
||||
|
||||
File.Delete(path);
|
||||
presenter.InvokeMethod("GetGuestPipelineCacheSource", "test");
|
||||
presenter.InvokeMethod("SaveGuestPipelineCaches");
|
||||
Assert.False(File.Exists(path));
|
||||
|
||||
presenter.InvokeMethod("SaveGuestPipelineCaches");
|
||||
presenter.InvokeMethod("DestroyGuestPipelineCaches");
|
||||
_ = ((Lazy<PipelineCache>)presenter.InvokeMethod("GetGuestPipelineCacheSource", "test")!).Value;
|
||||
presenter.InvokeMethod("SaveGuestPipelineCaches");
|
||||
Assert.True(File.Exists(path));
|
||||
|
||||
presenter.InvokeMethod("DestroyGuestPipelineCaches");
|
||||
_ = ((Lazy<PipelineCache>)presenter.InvokeMethod("GetGuestPipelineCacheSource", "test")!).Value;
|
||||
File.Delete(path);
|
||||
presenter.InvokeMethod("SaveGuestPipelineCaches");
|
||||
Assert.False(File.Exists(path));
|
||||
}
|
||||
finally
|
||||
{
|
||||
presenter.InvokeMethod("DestroyGuestPipelineCaches");
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ACheckpointWritesNewShardsAndWaitsForTheNextInterval()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue