Massively improve performance by rewriting the network core & rendering using burst jobs#864
Merged
Phantomical merged 11 commits intoJul 7, 2026
Conversation
Reference Unity.Burst, Unity.Collections, Unity.Mathematics and Unity.Jobs from KSPBurst so the network model can be rewritten against the job system, and pull in Krafs.Publicizer to expose UnityEngine.Object.m_CachedPtr (for thread-safe null checks) and List<T>._items (for an AsSpan helper). Expose internals to the RemoteTech.Tests and RemoteTech.InGameTests assemblies, bump the language version to 13, and replace the .slnx with a plain .sln.
Later work is going to need a number of utilities. This commit adds: * an ArrayMap class that stores keys/values in arrays internally, * an ArrayMinHeap struct that provides a burst-compatible min-heap, * a number of extension helper methods for various types * an ObjectHandle struct for passing managed types to burst jobs * a IJobParallelForBatchDefer interface and extensions
Replace the managed graph/route computation with an unmanaged pipeline built on the Unity job system. Satellite and antenna state is projected into native arrays (NetworkState) once per update, and connectivity, routing and the render geometry are computed by Burst jobs (NetworkUpdate, Edges, NetworkUpdateMath, and the line/cone/mark mesh jobs). Antennas publish their state through a pinned AntennaData registered with StateManager, so the jobs can read them without touching managed memory. ISatellite gains a flat SatelliteState snapshot and Antennas becomes an IReadOnlyList; NetworkLink/NetworkRoute become readonly struct views over the store rather than heap-allocated objects. The range models are unified behind IRangeModel so the job path and the managed path share one implementation. NetworkManager drives the pipeline and NetworkRenderer draws the computed meshes directly, replacing the per-frame NetworkCone/NetworkLine GameObjects.
RemoteTech.Tests runs the Burst/NativeArray math headlessly under xunit by backing native arrays with Marshal.AllocHGlobal (NativeArena), covering the range/geometry math. RemoteTech.InGameTests is a KSP.Testing plugin with an in-game runner UI that exercises the jobs against real Unity Collections (adjacency, edges, Dijkstra, mark/cone geometry, hashing).
Route the public API methods through the precomputed NetworkState instead of the old on-demand LINQ/route materialization: - Add a GetRoute passthrough to the NetworkManager read seam. - GetFirstHopToKSC / GetControlPath now read the groundOnly route directly via GetRoute rather than filtering + Min()-ing over BuildConnections. - HasDirectGroundStation / GetClosestDirectGroundStation now test the satellite's direct adjacency row (GetLinks) for a ground-station target. This treats any direct link to a ground station as qualifying (not only first hops of a shortest route) and drops the empty/self-route NRE and the empty-sequence Min() throw. - Replace the O(n) Satellites.Where(...).FirstOrDefault() guid scans with the O(1) Satellites[id] indexer; GetName / GetRangeDistance use the Network[id] indexer so ground stations still resolve. GetSignalDelayToSatellite's arbitrary A->B pathfind is left on NetworkPathfinder; NetworkState has no point-to-point equivalent yet.
Relay capability now determines whether a node may forward a signal (transit), not whether a link exists. IsEdgeTraversable previously required both endpoints of every edge to be relay-capable, which wrongly disconnected powered non-relay endpoints (e.g. passive-SPU vessels) — a route's own endpoints never forward, so they should not need to relay. ComputeAdjacencyLists now includes an edge whenever both endpoints are powered. The relay check moves into a per-node CanTransit bitmask: the multi-source Dijkstra records a node's score but only expands out of it when it can transit, and roots (route endpoints) always expand.
Replace GetSignalDelayToSatellite's on-demand A* (NetworkPathfinder) with a Burst NetworkPathfindJob run over the persisted NetworkState edge table, exposed as NetworkState.ShortestDelayBetween plus a NetworkManager passthrough. Add the NetworkUpdateMath.EncodePairIndex helper the job uses to index the triangular edge table.
The multi-path overlay drew every tree edge in the command-station forest, so a vessel hidden by the map view filter still showed its green route back to control. Seed the on-path set by walking the predecessor forest from each visible vessel instead, so a route is drawn only when a visible vessel sits at its far end.
The migration onto the Burst NetworkState left the old managed routing orphaned. Delete the A* NetworkPathfinder and the explicit-mode half of NetworkRoute it fed (the freestanding-path ctor, Empty, Contains, and the BidirectionalEdge it compared against), leaving the live value-type view into the Dijkstra forests. Drop the unused NetworkManager.FindNeighbors / ConnectionCache / Count, the NetworkState.GetRouteLength / GetRouteOrigin guid wrappers (callers use the internal index versions), the unused VesselSatellite.Connections seam, and the never-referenced Dish type.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The networking core of RemoteTech is quite slow, and the rendering is even slower. This commit rips all that out and replaces it with a set of burst-compiled jobs that run the network update in the background, along with more burst jobs that generate meshes for the network links and cone rendering. On my 200-vessel test save this is quite literally 20,000x faster.
While there are a few places that this affects the public API, this should be mostly backwards compatible. The external
APInamespace is completely unchanged, but some of the interfaces have had new methods added and there were a few[KSPField]s that have been converted to properties.Here's the big changes:
NetworkState.ScheduleNetworkUpdate. There are a couple of managed jobs that read in the state of vessel satellites, ground stations, and celestial bodies, and then a chain of native jobs that do all the steps of the update. Most of burst code is inNetworkUpdate, but there are other jobs that handle some of the parallel parts (BuildEdgesJobandMultiSourceDijkstraJob).NetworkStateinstance inNetworkManager. This is always one FixedUpdate behind the actual game state, since the new state is calculated in the background.OnPreCulland make a singleGraphics.DrawMeshcall with the whole mesh. The jobs get kicked off inLateUpdate, and so the whole thing overall ends up being quite cheap on CPU.Also, I have introduced two new dependencies:
KSPBurstandSerializationFix. This will limit RemoteTech to only being supported on 1.12. People using RT on older versions can continue to use older versions of RT.Put all together the end result is that the overhead of the network update is approximately fixed (about 0.2ms/FixedUpdate) and we can afford to run the whole update every time. There's still some work to do on performance elsewhere, but I think everything here is about as good as it is going to get.
Performance Numbers
All the numbers here are recorded on a 200-vessel, where every vessel is connected into the network. This is about the worst case possible, and likely to be worse than the save of any existing player using RemoteTech. The game becomes unplayable on the existing version long before anyone could get to this point.

Here's what the profiles look like with develop. In all cases this is with the map view open

And here's what the profile looks like on this branch

It's hard to see where RT is running in that image because it's too fast :), so here's some zoomed in views of the individual parts: