perf(bake): stream catalog and bound validation residency
This commit is contained in:
parent
b7b9aaa9dd
commit
90bf6bbf45
7 changed files with 217 additions and 84 deletions
|
|
@ -53,51 +53,94 @@ public sealed class EnvCellBakeCatalog
|
|||
ArgumentNullException.ThrowIfNull(sources);
|
||||
ArgumentNullException.ThrowIfNull(computeIdentity);
|
||||
|
||||
var byGeometry = new Dictionary<ulong, MutableGroup>();
|
||||
var fileIds = new HashSet<uint>();
|
||||
int cellCount = 0;
|
||||
var builder = new EnvCellBakeCatalogBuilder(computeIdentity);
|
||||
foreach (var source in sources)
|
||||
builder.Add(source);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
foreach (var source in sources.OrderBy(source => source.FileId))
|
||||
internal static EnvCellBakeCatalog Create(
|
||||
IReadOnlyList<EnvCellGeometryGroup> groups,
|
||||
int cellCount) =>
|
||||
new(groups, cellCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Incremental catalog builder used by the full bake so 729,888 EnvCell
|
||||
/// records and their surface lists are never retained at once.
|
||||
/// </summary>
|
||||
public sealed class EnvCellBakeCatalogBuilder
|
||||
{
|
||||
private readonly Func<uint, ushort, IReadOnlyList<ushort>, ulong> _computeIdentity;
|
||||
private readonly Dictionary<ulong, MutableGroup> _byGeometry = new();
|
||||
private readonly HashSet<uint> _fileIds = new();
|
||||
private int _cellCount;
|
||||
private bool _built;
|
||||
|
||||
public EnvCellBakeCatalogBuilder()
|
||||
: this(EnvCellGeometryIdentity.Compute)
|
||||
{
|
||||
}
|
||||
|
||||
public EnvCellBakeCatalogBuilder(
|
||||
Func<uint, ushort, IReadOnlyList<ushort>, ulong> computeIdentity)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(computeIdentity);
|
||||
_computeIdentity = computeIdentity;
|
||||
}
|
||||
|
||||
public void Add(EnvCellBakeSource source)
|
||||
{
|
||||
if (_built)
|
||||
throw new InvalidOperationException("the EnvCell bake catalog is already complete");
|
||||
|
||||
if (!_fileIds.Add(source.FileId))
|
||||
throw new InvalidDataException($"duplicate EnvCell file id 0x{source.FileId:X8}");
|
||||
|
||||
ulong geometryId = _computeIdentity(
|
||||
source.EnvironmentId,
|
||||
source.CellStructure,
|
||||
source.Surfaces);
|
||||
|
||||
if (_byGeometry.TryGetValue(geometryId, out var existing))
|
||||
{
|
||||
if (!fileIds.Add(source.FileId))
|
||||
throw new InvalidDataException($"duplicate EnvCell file id 0x{source.FileId:X8}");
|
||||
|
||||
var surfaces = source.Surfaces.ToArray();
|
||||
ulong geometryId = computeIdentity(
|
||||
source.EnvironmentId,
|
||||
source.CellStructure,
|
||||
surfaces);
|
||||
|
||||
if (byGeometry.TryGetValue(geometryId, out var existing))
|
||||
if (existing.EnvironmentId != source.EnvironmentId ||
|
||||
existing.CellStructure != source.CellStructure ||
|
||||
!SurfacesEqual(existing.Surfaces, source.Surfaces))
|
||||
{
|
||||
if (existing.EnvironmentId != source.EnvironmentId ||
|
||||
existing.CellStructure != source.CellStructure ||
|
||||
!existing.Surfaces.AsSpan().SequenceEqual(surfaces))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"EnvCell geometry identity collision 0x{geometryId:X16}: " +
|
||||
$"cell 0x{existing.FileIds[0]:X8} and cell 0x{source.FileId:X8} " +
|
||||
"have different environment/cell-structure/surface tuples");
|
||||
}
|
||||
|
||||
existing.FileIds.Add(source.FileId);
|
||||
throw new InvalidDataException(
|
||||
$"EnvCell geometry identity collision 0x{geometryId:X16}: " +
|
||||
$"cell 0x{existing.FileIds[0]:X8} and cell 0x{source.FileId:X8} " +
|
||||
"have different environment/cell-structure/surface tuples");
|
||||
}
|
||||
else
|
||||
{
|
||||
byGeometry.Add(
|
||||
|
||||
existing.FileIds.Add(source.FileId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_byGeometry.Add(
|
||||
geometryId,
|
||||
new MutableGroup(
|
||||
geometryId,
|
||||
new MutableGroup(
|
||||
geometryId,
|
||||
source.EnvironmentId,
|
||||
source.CellStructure,
|
||||
surfaces,
|
||||
new List<uint> { source.FileId }));
|
||||
}
|
||||
|
||||
cellCount++;
|
||||
source.EnvironmentId,
|
||||
source.CellStructure,
|
||||
source.Surfaces.ToArray(),
|
||||
new List<uint> { source.FileId }));
|
||||
}
|
||||
|
||||
var groups = byGeometry.Values
|
||||
_cellCount++;
|
||||
}
|
||||
|
||||
public EnvCellBakeCatalog Build()
|
||||
{
|
||||
if (_built)
|
||||
throw new InvalidOperationException("the EnvCell bake catalog is already complete");
|
||||
_built = true;
|
||||
|
||||
foreach (var group in _byGeometry.Values)
|
||||
group.FileIds.Sort();
|
||||
|
||||
var groups = _byGeometry.Values
|
||||
.Select(group => new EnvCellGeometryGroup(
|
||||
group.GeometryId,
|
||||
group.EnvironmentId,
|
||||
|
|
@ -107,7 +150,21 @@ public sealed class EnvCellBakeCatalog
|
|||
.OrderBy(group => group.FileIds[0])
|
||||
.ToArray();
|
||||
|
||||
return new EnvCellBakeCatalog(groups, cellCount);
|
||||
return EnvCellBakeCatalog.Create(groups, _cellCount);
|
||||
}
|
||||
|
||||
private static bool SurfacesEqual(
|
||||
ReadOnlySpan<ushort> expected,
|
||||
IReadOnlyList<ushort> actual)
|
||||
{
|
||||
if (expected.Length != actual.Count)
|
||||
return false;
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
if (expected[i] != actual[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private sealed record MutableGroup(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue