< Summary

Information
Class: Trailblazer.Pathing.NavigationOverlayFoldWork
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Map/Operations/NavigationOverlayFoldWork.cs
Line coverage
100%
Covered lines: 242
Uncovered lines: 0
Coverable lines: 242
Total lines: 410
Line coverage: 100%
Branch coverage
100%
Covered branches: 116
Total branches: 116
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Map/Operations/NavigationOverlayFoldWork.cs

#LineLine coverage
 1//=======================================================================
 2// NavigationOverlayFoldWork.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using GridForge.Grids.Topology;
 10
 11namespace Trailblazer.Pathing;
 12
 13/// <summary>Folds one atomic overlay transaction into persistent map roots across frames.</summary>
 14internal sealed class NavigationOverlayFoldWork
 15{
 16    private readonly NavigationOperationCandidate _source;
 17    private readonly NavigationOperationCandidate _working;
 18    private readonly NavigationOverlayTransaction _transaction;
 19    private readonly long _operationSequence;
 20    private readonly NavigationOperationLimits _limits;
 21    private readonly GridCellPrism[] _corridorPrisms;
 22    private readonly FixedMathSharp.Vector3d[] _corridorWaypoints;
 23    private readonly NavigationCellAddress[] _corridorAddresses;
 24    private readonly NavigationAddressStampSet _corridorAddressSet;
 25    private readonly NavigationOperationCandidate.MapState[] _priorStates;
 26    private readonly NavigationOperationCandidate.MapState[] _nextStates;
 27    private readonly string[] _changedMapIds;
 28    private int _mapIndex;
 29    private int _cellIndex;
 30    private int _connectionIndex;
 31    private int _transitionIndex;
 32    private NavigationOperationCandidate.MapState? _current;
 33    private NavigationMapOverlayState? _overlay;
 34    private PersistentVoxelIndexMap<byte>? _dynamicAddresses;
 35    private int _dependencyMapIndex;
 36    private int _dependencyStage;
 37    private int _dependencyIndex;
 30938    private bool _removeDependencies = true;
 39    private int _validationMapIndex;
 30940    private int _validationSourceIndex = -1;
 41    private int _validationStage;
 42    private int _validationEdgeIndex;
 43    private Stage _stage;
 44    private NavigationOperationCandidate.ExplicitConnectionRefreshWork? _explicitRefresh;
 45    private long _displacedMapStatePayloadBytes;
 46    private int _displacedMapStatePayloadPages;
 47
 30948    internal NavigationOverlayFoldWork(
 30949        NavigationOperationCandidate source,
 30950        NavigationOverlayTransaction transaction,
 30951        long operationSequence,
 30952        NavigationOperationLimits limits,
 30953        GridCellPrism[] corridorPrisms,
 30954        FixedMathSharp.Vector3d[] corridorWaypoints,
 30955        NavigationCellAddress[] corridorAddresses,
 30956        NavigationAddressStampSet corridorAddressSet)
 57    {
 30958        _source = source;
 30959        _working = source.Clone();
 30960        _transaction = transaction;
 30961        _operationSequence = operationSequence;
 30962        _limits = limits;
 30963        _corridorPrisms = corridorPrisms;
 30964        _corridorWaypoints = corridorWaypoints;
 30965        _corridorAddresses = corridorAddresses;
 30966        _corridorAddressSet = corridorAddressSet;
 30967        int mapCount = transaction.MapSpan.Length;
 30968        _priorStates = new NavigationOperationCandidate.MapState[mapCount];
 30969        _nextStates = new NavigationOperationCandidate.MapState[mapCount];
 30970        _changedMapIds = new string[mapCount];
 30971    }
 72
 588273    internal NavigationOperationCandidate Candidate => _working;
 74
 75    internal long DisplacedExplicitPayloadBytes =>
 87376        _explicitRefresh?.DisplacedSourcePayloadBytes ?? 0L;
 77
 78    internal int DisplacedExplicitPayloadPages =>
 87379        _explicitRefresh?.DisplacedSourcePayloadPages ?? 0;
 80
 87081    internal long DisplacedMapStatePayloadBytes => _displacedMapStatePayloadBytes;
 82
 87083    internal int DisplacedMapStatePayloadPages => _displacedMapStatePayloadPages;
 84
 87085    internal long RetainedBytes => checked(
 87086        160L
 87087        + _transaction.EstimatedDescriptorBytes
 87088        + ((long)(_priorStates.Length + _nextStates.Length + _changedMapIds.Length)
 87089            * IntPtr.Size)
 87090        + _working.WorkCopiedPersistentBytes
 87091        + (_explicitRefresh?.RetainedBytes ?? 0)
 87092        + GetPendingMapStatePayloadBytes());
 93
 87094    internal int PersistentPageCount => checked(
 87095        1
 87096        + _working.WorkCopiedPersistentPages
 87097        + (_explicitRefresh?.PersistentPageCount ?? 0)
 87098        + GetPendingMapStatePayloadPages());
 99
 100    internal bool Advance(
 101        MaintenanceWorkMeter meter,
 102        out NavigationOperationRejection rejection)
 103    {
 494104        if (_stage == Stage.Fold)
 105        {
 384106            ReadOnlySpan<NavigationMapOverlayDelta> maps = _transaction.MapSpan;
 728107            while (_mapIndex < maps.Length)
 108            {
 432109                NavigationMapOverlayDelta delta = maps[_mapIndex];
 432110                if (_current == null)
 111                {
 357112                    if (!_source.TryGetState(delta.MapId, out _current) || _current == null)
 113                    {
 1114                        rejection = NavigationOperationRejection.MissingMap;
 1115                        return true;
 116                    }
 356117                    _overlay = _current.Overlay;
 356118                    _dynamicAddresses = _current.DynamicAddresses;
 119                }
 431120                ReadOnlySpan<NavigationCellOverlayOperation> cells = delta.CellSpan;
 17638121                while (_cellIndex < cells.Length)
 122                {
 17287123                    if (!meter.TryConsumeOverlaySlots(1))
 124                    {
 76125                        rejection = NavigationOperationRejection.None;
 76126                        return false;
 127                    }
 17211128                    NavigationCellOverlayOperation change = cells[_cellIndex++];
 17211129                    if (!_current.Map.GridBinding.IsValidIndex(change.Index)
 17211130                        || (change.Kind == NavigationCellOverlayOperationKind.Set
 17211131                            && change.Cell.Area.Value >= _working.NavigationAreaCount))
 132                    {
 4133                        rejection = NavigationOperationRejection.ValidationFailed;
 4134                        return true;
 135                    }
 17207136                    _overlay = _overlay!.Apply(change, _operationSequence);
 17207137                    _working.RecordPersistentCopies(_overlay.LastApplyCopiedNodeCount);
 17207138                    if (!_current.Map.ContainsCell(change.Index)
 17207139                        && (change.Kind == NavigationCellOverlayOperationKind.Set
 17207140                            || (change.Kind == NavigationCellOverlayOperationKind.Suppress
 17207141                                && _current.Map.DefaultCell.HasValue)))
 142                    {
 17038143                        _dynamicAddresses = _dynamicAddresses!.Set(
 17038144                            change.Index,
 17038145                            0,
 17038146                            out int copiedNodes);
 17038147                        _working.RecordPersistentCopies(copiedNodes);
 148                    }
 149                }
 351150                ReadOnlySpan<NavigationConnectionOverlayOperation> connections = delta.ConnectionSpan;
 4509151                while (_connectionIndex < connections.Length)
 152                {
 4160153                    if (!meter.TryConsumeOverlaySlots(1))
 154                    {
 1155                        rejection = NavigationOperationRejection.None;
 1156                        return false;
 157                    }
 4159158                    NavigationConnectionOverlayOperation change = connections[_connectionIndex++];
 4159159                    if (change.Kind == NavigationConnectionOverlayOperationKind.Upsert
 4159160                        && change.Connection!.Witnesses.Count > _limits.MaxCorridorCells - 2)
 161                    {
 1162                        rejection = NavigationOperationRejection.CapacityExceeded;
 1163                        return true;
 164                    }
 4158165                    _overlay = _overlay!.Apply(change, _operationSequence);
 4158166                    _working.RecordPersistentCopies(_overlay.LastApplyCopiedNodeCount);
 167                }
 349168                ReadOnlySpan<TraversalTransitionOverlayOperation> transitions = delta.TransitionSpan;
 4466169                while (_transitionIndex < transitions.Length)
 170                {
 4118171                    if (!meter.TryConsumeOverlaySlots(1))
 172                    {
 1173                        rejection = NavigationOperationRejection.None;
 1174                        return false;
 175                    }
 4117176                    _overlay = _overlay!.Apply(transitions[_transitionIndex++], _operationSequence);
 4117177                    _working.RecordPersistentCopies(_overlay.LastApplyCopiedNodeCount);
 178                }
 179
 348180                NavigationOperationCandidate.MapState next = new(
 348181                    _current.Map,
 348182                    _current.BakeVersion,
 348183                    _current.PreparedMapRetainedBytes,
 348184                    _overlay!,
 348185                    _current.DynamicSlotGeneration,
 348186                    _dynamicAddresses!,
 348187                    _current.BakedCellLookup);
 348188                _working.RecordMapStateOwnership(
 348189                    delta.MapId,
 348190                    next,
 348191                    _source,
 348192                    ref _displacedMapStatePayloadBytes,
 348193                    ref _displacedMapStatePayloadPages);
 348194                rejection = _working.ReplaceOverlayState(
 348195                    _current,
 348196                    next,
 348197                    _limits);
 348198                if (rejection != NavigationOperationRejection.None)
 4199                    return true;
 344200                _priorStates[_mapIndex] = _current;
 344201                _nextStates[_mapIndex] = next;
 344202                _changedMapIds[_mapIndex] = delta.MapId;
 344203                _mapIndex++;
 344204                _cellIndex = 0;
 344205                _connectionIndex = 0;
 344206                _transitionIndex = 0;
 344207                _current = null;
 344208                _overlay = null;
 344209                _dynamicAddresses = null;
 210            }
 296211            _stage = Stage.Dependencies;
 212        }
 213
 406214        if (_stage == Stage.Dependencies)
 215        {
 983216            while (_dependencyMapIndex < _priorStates.Length)
 217            {
 687218                NavigationOperationCandidate.MapState state = _removeDependencies
 687219                    ? _priorStates[_dependencyMapIndex]
 687220                    : _nextStates[_dependencyMapIndex];
 687221                if (!AdvanceDependencies(state, _removeDependencies, meter))
 222                {
 1223                    rejection = NavigationOperationRejection.None;
 1224                    return false;
 225                }
 686226                if (_removeDependencies)
 227                {
 343228                    _removeDependencies = false;
 343229                    ResetDependencyCursor();
 343230                    continue;
 231                }
 343232                _removeDependencies = true;
 343233                _dependencyMapIndex++;
 343234                ResetDependencyCursor();
 235            }
 296236            _stage = Stage.Validation;
 237        }
 238
 1097239        while (_validationMapIndex < _nextStates.Length)
 240        {
 241            NavigationOperationCandidate.MapState state;
 699242            if (_validationSourceIndex < 0)
 346243                state = _nextStates[_validationMapIndex];
 244            else
 245            {
 353246                string target = _changedMapIds[_validationMapIndex];
 353247                int sourceCount = _working.GetIncomingSourceCount(target);
 353248                if (_validationSourceIndex >= sourceCount)
 249                {
 339250                    _validationMapIndex++;
 339251                    _validationSourceIndex = -1;
 339252                    continue;
 253                }
 14254                string sourceId = _working.GetIncomingSource(target, _validationSourceIndex);
 14255                bool hasSource = _working.TryGetState(
 14256                    sourceId,
 14257                    out NavigationOperationCandidate.MapState? source);
 258                System.Diagnostics.Debug.Assert(hasSource && source != null);
 14259                state = source!;
 260            }
 360261            if (!AdvanceValidation(state, meter, out bool valid))
 262            {
 3263                rejection = NavigationOperationRejection.None;
 3264                return false;
 265            }
 357266            if (!valid)
 267            {
 4268                rejection = NavigationOperationRejection.ValidationFailed;
 4269                return true;
 270            }
 353271            if (_validationSourceIndex < 0)
 339272                _validationSourceIndex = 0;
 273            else
 14274                _validationSourceIndex++;
 275        }
 398276        _explicitRefresh ??= _working.BeginExplicitConnectionRefresh(
 398277            _transaction,
 398278            _source.ExplicitConnections,
 398279            _corridorPrisms,
 398280            _corridorWaypoints,
 398281            _corridorAddresses,
 398282            _corridorAddressSet);
 398283        if (!_explicitRefresh.Advance(meter))
 284        {
 107285            rejection = NavigationOperationRejection.None;
 107286            return false;
 287        }
 291288        if (!_explicitRefresh.IsValid)
 289        {
 3290            rejection = NavigationOperationRejection.ValidationFailed;
 3291            return true;
 292        }
 288293        _stage = Stage.Complete;
 288294        rejection = NavigationOperationRejection.None;
 288295        return true;
 296    }
 297
 298    private long GetPendingMapStatePayloadBytes()
 299    {
 870300        long bytes = 0;
 870301        if (_current == null)
 615302            return bytes;
 255303        if (!ReferenceEquals(_overlay, _current.Overlay))
 85304            bytes = checked(bytes + _overlay!.RetainedBytes);
 255305        if (!ReferenceEquals(_dynamicAddresses, _current.DynamicAddresses))
 306        {
 16307            bytes = checked(bytes + _dynamicAddresses!.RetainedBytes);
 308        }
 255309        return bytes;
 310    }
 311
 312    private int GetPendingMapStatePayloadPages()
 313    {
 870314        int pages = 0;
 870315        if (_current == null)
 615316            return pages;
 255317        if (!ReferenceEquals(_overlay, _current.Overlay))
 85318            pages = checked(pages + _overlay!.PersistentNodeCount);
 255319        if (!ReferenceEquals(_dynamicAddresses, _current.DynamicAddresses))
 320        {
 16321            pages = checked(pages + _dynamicAddresses!.PersistentNodeCount);
 322        }
 255323        return pages;
 324    }
 325
 326    private bool AdvanceDependencies(
 327        NavigationOperationCandidate.MapState state,
 328        bool remove,
 329        MaintenanceWorkMeter meter)
 330    {
 2059331        while (_dependencyStage < 2)
 332        {
 1373333            int count = _dependencyStage == 0
 1373334                ? state.Map.TransitionSpan.Length
 1373335                : state.Overlay.TransitionCount;
 5526336            while (_dependencyIndex < count)
 337            {
 4154338                TraversalTransitionOverlayOperation overlay = _dependencyStage == 1
 4154339                    ? state.Overlay.GetTransitionAt(_dependencyIndex)
 4154340                    : default;
 4154341                if (_dependencyStage == 1
 4154342                    && overlay.Kind != TraversalTransitionOverlayOperationKind.Upsert)
 343                {
 24344                    _dependencyIndex++;
 24345                    continue;
 346                }
 4130347                if (!meter.TryConsumeDependencyEntries(1))
 1348                    return false;
 4129349                string destination = _dependencyStage == 0
 4129350                    ? state.Map.TransitionSpan[_dependencyIndex].Destination.MapId
 4129351                    : overlay.Transition.Destination.MapId;
 4129352                _working.UpdateIncomingSourceForWork(destination, state.Map.MapId, remove);
 4129353                _dependencyIndex++;
 354            }
 1372355            _dependencyStage++;
 1372356            _dependencyIndex = 0;
 357        }
 686358        return true;
 359    }
 360
 361    private bool AdvanceValidation(
 362        NavigationOperationCandidate.MapState state,
 363        MaintenanceWorkMeter meter,
 364        out bool valid)
 365    {
 1070366        while (_validationStage < 2)
 367        {
 717368            int count = _validationStage == 0
 717369                ? state.Map.TransitionSpan.Length
 717370                : state.Overlay.TransitionCount;
 8954371            while (_validationEdgeIndex < count)
 372            {
 8244373                if (!meter.TryConsumeExplicitEdges(1))
 374                {
 3375                    valid = true;
 3376                    return false;
 377                }
 8241378                if (!_working.ValidateTransitionForWork(
 8241379                        state,
 8241380                        overlay: _validationStage == 1,
 8241381                        _validationEdgeIndex++,
 8241382                        _changedMapIds,
 8241383                        _nextStates))
 384                {
 4385                    valid = false;
 4386                    return true;
 387                }
 388            }
 710389            _validationStage++;
 710390            _validationEdgeIndex = 0;
 391        }
 353392        _validationStage = 0;
 353393        valid = true;
 353394        return true;
 395    }
 396
 397    private void ResetDependencyCursor()
 398    {
 686399        _dependencyStage = 0;
 686400        _dependencyIndex = 0;
 686401    }
 402
 403    private enum Stage
 404    {
 405        Fold,
 406        Dependencies,
 407        Validation,
 408        Complete
 409    }
 410}