Game.Rendering.RouteBufferSystem
Assembly:
Namespace: Game.Rendering
Type: class
Base: GameSystemBase, IPreDeserialize
Summary:
Manages GPU-side route segment buffers and materials used for rendering route paths (e.g. transit/vehicle routes). It tracks Route entities, builds per-route segment data (Bezier segments and nodes) on worker threads (Burst-compiled IJobParallelFor), and exposes ComputeBuffer + Material + bounds/size for rendering. The system uses Unity's DOTS APIs (Entity queries, BufferLookup, ComponentLookup), Burst jobs, and Native containers. It reacts to route updates and (re)allocates/updates managed ComputeBuffers and Materials for each routed prefab.
Fields
-
private RenderingSystem m_RenderingSystem
Holds reference to the RenderingSystem (used for frame/time values when scheduling the UpdateBufferJob). -
private PrefabSystem m_PrefabSystem
Reference to the PrefabSystem used to resolve RoutePrefab instances for route entities. -
private EntityQuery m_UpdatedRoutesQuery
Query for routes that need updating (Updated / LivePath / Deleted / PathUpdated events). -
private EntityQuery m_AllRoutesQuery
Query that matches all Route entities (used when loading to initialize buffers). -
private EntityQuery m_RouteConfigQuery
Query used to get route configuration data (e.g. fallback missing route prefab). -
private List<ManagedData> m_ManagedData
Managed (UnityEngine) objects per-route: Material and ComputeBuffer references. Lifetimes must be disposed manually. -
private NativeList<NativeData> m_NativeData
Native-side data for each route (UnsafeList of SegmentData, bounds, length, updated flag, entity reference). Used by the worker job. -
private Stack<int> m_FreeBufferIndices
Pool of free indices for reusing managed/native buffer slots when routes are deleted. -
private JobHandle m_BufferDependencies
JobHandle for the scheduled UpdateBufferJob. Completed before accessing managed data. -
private bool m_Loaded
Flag set by PreDeserialize to indicate initial load; used to initialize all routes on next update. -
private TypeHandle __TypeHandle
Generated structure holding component/type handles used by the system (internal DOTS boilerplate).
Nested types (brief):
-
ManagedData(private class)
Holds Material, ComputeBuffer, Vector4 size, original render queue and an "updated" flag. Provides Initialize(RoutePrefab) and Dispose() to create/destroy UnityEngine.Object resources. -
NativeData(private struct)
Holds UnsafeListm_SegmentData, Bounds3 m_Bounds, Entity m_Entity, float m_Length, and a bool m_Updated. Has Initialize(Entity) and Dispose(). -
SegmentData(private struct)
Layout that represents one route segment or node (float4x4 curve encoded, position, size factors, opacity, divided opacity, broken flag). This struct is copied into the ComputeBuffer for rendering. -
UpdateBufferJob(private Burst-compiled struct)
IJobParallelFor that computes/updates SegmentData lists for each updated route (reads many ECS lookups and buffers). It creates and manipulates Native containers, combines segment pieces, computes bounds and per-segment attributes, handles shared curve deduplication, and writes into NativeData.m_SegmentData.
Other nested helper structs: CurveKey, CurveValue, SourceKey (used inside the job to dedupe/merge segments).
Properties
- (none public)
This system exposes no public properties. Access to per-route buffers/materials is via GetBuffer(int index, out ...).
Constructors
public RouteBufferSystem()
Default constructor (preserved for runtime). Typical initialization is done in OnCreate.
Methods
-
protected override void OnCreate()
Initializes references to RenderingSystem and PrefabSystem and creates entity queries used by the system (m_UpdatedRoutesQuery, m_AllRoutesQuery, m_RouteConfigQuery). Call base.OnCreate(). -
protected override void OnDestroy()
Calls Clear(), disposes native lists (m_NativeData) and completes outstanding jobs before destruction. -
public void PreDeserialize(Context context)
Implements IPreDeserialize. Called before deserialization/load to clear existing buffers and mark the system as loaded so it initializes buffers for all routes on the next update. -
private void Clear()
Disposes/cleans managed Materials and ComputeBuffers, clears free index pool, completes outstanding jobs and disposes native data lists. Safe to call on load/teardown. -
public unsafe void GetBuffer(int index, out Material material, out ComputeBuffer segmentBuffer, out int originalRenderQueue, out Bounds bounds, out Vector4 size)
Retrieves the Material and ComputeBuffer for a given buffer index. Completes the scheduled UpdateBufferJob, updates (or creates) the ComputeBuffer from the NativeData.m_SegmentData if needed, and returns bounds and the size vector. Callers must ensure they pass a valid index (RouteBufferIndex.m_Index) and that the system has finished its job dependencies. -
private bool GetLoaded()
Internal helper to flip the m_Loaded flag once (returns true the first time after PreDeserialize). -
protected override void OnUpdate()
Main update loop. Gathers route entities to initialize or mark for update, allocates or reuses ManagedData/NativeData entries, sets updated flags for routes that changed, and schedules the Burst UpdateBufferJob (IJobParallelFor) to build segment lists. Maintains m_BufferDependencies and base.Dependency accordingly. -
private void __AssignQueries(ref SystemState state)
Compiler-generated helper used in OnCreateForCompiler (DOTS boilerplate). -
protected override void OnCreateForCompiler()
Compiler helper for DOTS-generated code; sets up type handles. -
Nested: ManagedData.Initialize(RoutePrefab) / Dispose()
Create a copy of the prefab Material (so each route material is unique), set m_Size vector from prefab width/segment length, and dispose resources on deletion. -
Nested: NativeData.Initialize(Entity) / Dispose()
Initialize entity reference and dispose the UnsafeList when no longer used. -
Nested (UpdateBufferJob.Execute and helpers):
- Execute(int index): For each updated NativeData element builds the list of segments (dedupes shared curves, slices curve sources, computes opacities/size factors, computes bounds and total route length).
- AddSegment(...): Adds or reuses a SegmentData in the UnsafeList, handles shared curve coalescing via internal NativeHashMap.
- GetSizeFactor(int sharedCount): Heuristic to reduce thickness when multiple routes share the same curve.
- AddNode(...): Add a node (waypoint) entry as a degenerate SegmentData so the renderer can draw waypoint markers.
Notes about multi-threading & memory: - UpdateBufferJob uses many ReadOnly lookups and Native containers and is Burst-compiled to run in parallel. The system completes the job (m_BufferDependencies.Complete()) before any managed UnityEngine objects (ComputeBuffer / Material) are read or modified. - NativeData.m_SegmentData is created as an UnsafeList with Allocator.Persistent and must be disposed explicitly (Clear/OnDestroy). - ManagedData holds UnityEngine.Object instances; Dispose must be called on the main thread.
Usage Example
// Typical pattern inside a rendering or UI system that wants to draw routes:
// - Each Route entity has a RouteBufferIndex component containing an index to this system's buffers.
// - Call GetBuffer(index, out material, out segmentBuffer, out originalRenderQueue, out bounds, out size)
// to obtain the data needed to issue a draw call using the provided material and ComputeBuffer.
// Example: retrieving the route buffer for rendering
int index = routeBufferIndexComponent.m_Index;
if (index >= 0)
{
Material mat;
ComputeBuffer segBuffer;
int originalQueue;
Bounds bounds;
Vector4 size;
routeBufferSystem.GetBuffer(index, out mat, out segBuffer, out originalQueue, out bounds, out size);
if (segBuffer != null && mat != null)
{
// Set shader properties and dispatch draw (pseudo-code)
mat.SetVector("_Size", size);
mat.SetBuffer("_Segments", segBuffer);
Graphics.DrawProcedural(mat, bounds, MeshTopology.Triangles, /*vertexCount*/ 6 * segBuffer.count);
}
}
Additional tips for modders: - If you modify RoutePrefab shader/material parameters or segment struct layout, ensure SegmentData struct and the shader buffer layout remain compatible. - Avoid accessing ManagedData (Material/ComputeBuffer) from jobs — always complete m_BufferDependencies before doing so. - PreDeserialize(Context) will clear existing buffers on load; if you create routes during deserialization, expect the system to reinitialize buffers on the next frame.