Skip to content

Game.Rendering.BatchMeshSystem

Assembly: Assembly-CSharp
Namespace: Game.Rendering

Type: class

Base: GameSystemBase

Summary:
Manages loading, caching, generation and unloading of mesh data used by rendering batches (objects, nets, lanes, zones) in Cities: Skylines 2. Responsible for: - tracking per-batch loading priority and loading state, - requesting GeometryAsset data asynchronously, - caching mesh/geometry data into ECS buffers, - generating runtime Mesh instances for procedurally generated or base meshes, - providing default fallback meshes, - handling shape/blend-weight data in a global structured GraphicsBuffer, - enforcing a memory budget and optionally strict unloading rules.

Intended for internal engine/mod interactions that add/remove batches, change priorities or query loading state. Exposes methods to get NativeLists used by scheduling jobs that compute priorities.


Fields

  • private GeometryAssetLoadingSystem m_GeometryLoadingSystem
    Reference to the global system that performs asynchronous geometry asset loading requests.

  • private BatchManagerSystem m_BatchManagerSystem
    Reference to the batch manager system used to set meshes/materials for managed batches.

  • private PrefabSystem m_PrefabSystem
    Prefab system used to obtain RenderPrefab instances.

  • private Mesh m_DefaultObjectMesh, m_DefaultBaseMesh, m_DefaultLaneMesh, m_ZoneBlockMesh, m_ZoneLodMesh, m_DefaultEdgeMesh, m_DefaultNodeMesh, m_DefaultRoundaboutMesh
    Cached default Mesh instances used as fallbacks when a real mesh is unloaded or not available.

  • private List<Mesh> m_GeneratedMeshes
    Mesh objects generated at runtime (for generated base meshes, net compositions, etc).

  • private List<int> m_FreeMeshIndices
    Pool indices for reuse of generated meshes.

  • private HashSet<GeometryAsset> m_UnloadGeometryAssets
    GeometryAssets pending partial unload.

  • private HashSet<GeometryAsset> m_LoadingGeometries
    GeometryAssets currently being requested/loaded.

  • private Dictionary<Entity, CacheInfo> m_CachingMeshes
    Pending caching operations keyed by mesh entity (CacheInfo holds command buffer and JobHandle).

  • private Dictionary<Entity, MeshInfo> m_MeshInfos
    Registered mesh metadata for entities (size in memory, generated mesh index, shape allocations, batch count, etc).

  • private NativeList<int> m_BatchPriority
    NativeList of per-batch priority values (used by LoadingPriorityJob).

  • private NativeList<MeshLoadingState> m_LoadingState
    NativeList of per-batch loading states (None, Pending, Loading, Copying, Complete, Obsolete, etc).

  • private NativeList<LoadingData> m_LoadingData
    NativeList of batches queued to load (sorted by priority).

  • private NativeList<LoadingData> m_UnloadingData
    NativeList of batches queued to unload (sorted by priority).

  • private NativeList<Entity> m_GenerateMeshEntities
    Entities that need Mesh generation this frame (used to allocate writable mesh data).

  • private NativeHeapAllocator m_ShapeAllocator
    Allocator used to store shape/blend-weight data blocks (units are 8-byte elements).

  • private JobHandle m_PriorityDeps, m_StateDeps, m_GenerateMeshDeps
    Job handles used to track dependencies between scheduling of priority/state jobs and mesh generation.

  • private GraphicsBuffer m_ShapeBuffer
    GPU-side structured buffer ("shapeBuffer") holding shape data (64-bit chunks).

  • private Mesh.MeshDataArray m_GenerateMeshDataArray
    Temporary MeshDataArray for bulk mesh generation via Mesh.ApplyAndDisposeWritableMeshData.

  • private int m_ShapeCount
    Count of shape allocations.

  • private int m_PriorityLimit
    Runtime priority limit used to throttle loading when hitting memory budget.

  • private bool m_AddMeshes
    Internal flag indicating there are batches ready to be "copied" into managed batches (i.e., meshes/materials to be set).

  • constant fields: various strings and numeric limits (MAX_LOADING_COUNT = 30, MIN_BATCH_PRIORITY = -1000000, SHAPEBUFFER_ELEMENT_SIZE = 8, default memory budget = 1610612736, etc).


Properties

  • public ulong memoryBudget { get; set; }
    Total memory budget (bytes) used to decide when to unload meshes. Default: 1610612736 (1.5 GiB).

  • public bool strictMemoryBudget { get; set; }
    If true, strict checks will prevent starting loads that would exceed the budget. Default: false.

  • public bool enableMeshLoading { get; set; }
    If false, loading priority/job scheduling is disabled; prevents LoadingPriorityJob from running. Default: true.

  • public bool forceMeshUnloading { get; set; }
    When true, unconditional unloading is allowed even if it would normally be avoided. Default: false.

  • public ulong totalSizeInMemory { get; private set; }
    Total estimated memory used by cached mesh/geometry data (bytes).

  • public int loadedMeshCount => m_MeshInfos.Count
    Number of distinct mesh entities currently tracked/loaded.

  • public int loadingRemaining { get; private set; }
    Number of batches currently still queued for loading (m_LoadingData.Length at last evaluation).


Constructors

  • public BatchMeshSystem()
    Default constructor. The system is [Preserve]-marked and does initialization in OnCreate. No runtime arguments.

Methods

  • protected override void OnCreate()
    Initializes internal systems and data structures: obtains GeometryAssetLoadingSystem, BatchManagerSystem and PrefabSystem; creates native lists, shape allocator and sets default memory settings. Should be called by the world when the system is created.

  • protected override void OnDestroy()
    Cleans up meshes, generated mesh instances, pending caching operations, native lists, job handles (completes), shape buffer, and shape allocator. Ensures resources are freed when the system is removed.

  • protected override void OnUpdate()
    Main update loop. Calls LoadMeshes, UnloadMeshes and GenerateMeshes each frame. Tracks loading/unloading memory accounting.

  • public void ReplaceMesh(Entity oldMesh, Entity newMesh)
    Replace references from old mesh entity to new mesh entity. Copies buffer components (MeshVertex, MeshIndex, MeshNode, MeshNormal) and updates internal m_MeshInfos entry (if present). Updates ManagedBatches to replace mesh in existing batches. Completes and cleans up any pending caching for the old mesh.

  • public Mesh GetDefaultMesh(MeshType type, BatchFlags flags, GeneratedType generatedType)
    Returns a default/fallback Mesh for given type/flags. Lazily creates default meshes (object, net edge/node/roundabout, lane, zone block/lod, object base).

  • public NativeList<int> GetBatchPriority(out JobHandle dependencies)
    Returns the NativeList of batch priorities and the job handle that must be waited on (m_PriorityDeps) for correct access.

  • public NativeList<MeshLoadingState> GetLoadingState(out JobHandle dependencies)
    Returns the NativeList of batch loading states and the job handle that must be waited on (m_StateDeps).

  • public void AddBatchPriorityWriter(JobHandle dependencies)
    Register a job handle that writes to the priority list; stored in m_PriorityDeps.

  • public void AddLoadingStateReader(JobHandle dependencies)
    Register a job handle that reads the loading state; stored in m_StateDeps.

  • public void UpdateMeshes()
    Convenience: calls AddMeshes (to set meshes/materials on managed batches), UpdateMeshesForAddedInstances (for newly added instances) and UnloadMeshAndGeometryAssets(). Use this to force immediate mesh assignment steps.

  • public void CompleteMeshes()
    Completes scheduled mesh generation jobs, constructs Mesh instances from writable mesh data and uploads them to GPU (optionally marking them non-readable). Called after GenerateMeshes scheduling to finalize generated meshes.

  • public void UpdateBatchPriorities()
    Schedules LoadingPriorityJob (Burst-compiled) to update the lists m_LoadingData/m_UnloadingData and adjust priorities based on m_BatchPriority and m_LoadingState. Only runs when enableMeshLoading is true. Updates m_PriorityDeps and m_StateDeps.

  • public void CompleteCaching()
    Iterates pending m_CachingMeshes and completes any caching whose JobHandle is done; plays back entity command buffers and queues geometry assets for partial unload if any. Removes completed cache entries.

  • public void AddBatch(CustomBatch batch, int batchIndex)
    Register a new batch index in the internal lists; initializes m_BatchPriority and m_LoadingState at that index. Detects default/base flags to mark state as Default where appropriate.

  • public void RemoveBatch(CustomBatch batch, int batchIndex)
    Removes a registered batch; updates/unregisters mesh infos and shape allocations, possibly destroys generated Mesh if last batch referencing it. Cleans up m_LoadingData/m_UnloadingData entries for that batch and resets lists at that index.

  • public void UpdateMeshesForAddedInstances()
    Called to set meshes/materials for newly added instances. Iterates added instances from NativeBatchInstances and sets meshes/materials for managed batches if a loaded mesh info exists.

  • (Private) LoadMeshes(ref ulong loadingMemorySize, ref ulong neededMemorySize)
    Main loader routine: iterates m_LoadingData and attempts to load/cache the referenced meshes/geometry. Requests GeometryAsset loads when needed (up to 30 concurrent), performs caching (CacheMeshData) when assets are ready, creates generated Mesh placeholders when required, and updates m_MeshInfos. Updates m_AddMeshes flag and memory accounting. Honors strictMemoryBudget when deciding to start loads.

  • (Private) UnloadMeshes(ulong loadingMemorySize, ulong neededMemorySize)
    Performs unloading of meshes when memory budget would be exceeded (or when forceMeshUnloading). Iterates m_UnloadingData and replaces batch meshes with defaults, releases generated Mesh objects and shape allocations, and uncaches ECS mesh data as necessary.

  • (Private) UnloadMeshAndGeometryAssets()
    Requests partial unload for any GeometryAssets queued in m_UnloadGeometryAssets.

  • (Private) CacheMeshData(RenderPrefab meshPrefab, GeometryAsset asset, Entity entity, MeshType type)
    Begins an asynchronous cache operation for the given mesh entity depending on type (Object/Lane or Net). Adds a CacheInfo with an EntityCommandBuffer and JobHandle returned by helpers (ObjectMeshHelpers/NetMeshHelpers) and records it in m_CachingMeshes.

  • (Private) CacheMeshData(Mesh mesh, Entity entity, MeshType type)
    Similar to above but for raw Mesh instances (non-prefab) — sets up caching helpers and stores command buffer for playback.

  • (Private) UncacheMeshData(Entity mesh, MeshType type)
    Schedules uncache of ECS-mesh data via ObjectMeshHelpers/NetMeshHelpers and adds a cache entry to be completed later.

  • (Private) GenerateMeshes()
    If there are m_GenerateMeshEntities, allocates writable MeshDataArray and schedules mesh generation via BatchMeshHelpers.GenerateMeshes. The job handle is stored in base.Dependency (system dependency) to integrate with ECS job system.

  • (Private) AddMeshes()
    After caching/generation is done and m_AddMeshes is set, this assigns Mesh and Material to managed batches for every LoadingData entry in Copying state and marks them Complete.

  • (Private) AddShapeData(RenderPrefab meshPrefab) -> ShapeAllocation[]
    Reads shape (blend-weight) data from the GeometryAsset and allocates blocks from m_ShapeAllocator for each mesh in the prefab that has shape data. Writes shape data into the GPU shapeBuffer. Returns an array of ShapeAllocation for later SetShapeParameters.

  • (Private) RemoveShapeData(ShapeAllocation[] allocations)
    Releases shape allocations back to m_ShapeAllocator and updates shape count.

  • public void SetShapeParameters(MaterialPropertyBlock customProps, Entity sharedMeshEntity, int subMeshIndex)
    Look up stored ShapeAllocation for the mesh entity + submesh and write shape extents/stride/offset vectors into the material property block (two vectors). Used when rendering blend-shape / blend-weight requiring shapeBuffer access.

  • public void GetShapeStats(out uint allocatedSize, out uint bufferSize, out uint count)
    Returns shape allocator metrics in bytes (allocated size and buffer size are returned as bytes: m_ShapeAllocator.UsedSpace * 8, Size * 8) and number of allocations.

  • (Private) SetShapeParameters(CustomBatch batch, ShapeAllocation[] allocations)
    Helper to set batch.customProps and call BatchPropertyUpdated(batch) to push changes into native batch defaults.

  • (Private) BatchPropertyUpdated(CustomBatch batch)
    Writes material defaults and property blocks into the managed batch system (gets property data, obtains correct optional properties, writes defaults into native batch defaults accessor.)

  • (Private) ResizeShapeBuffer()
    Resizes the GPU structured buffer that backs shape data to match the NativeHeapAllocator size. Copies existing data when resizing up.

  • (Private) EstimateSizeInMemory(RenderPrefab meshPrefab) -> ulong
    Heuristic to estimate memory usage for a RenderPrefab (indexCount * index element size + vertexCount * per-vertex size). Used for budget estimation before geometry is fully loaded.

  • (Private) GetSizeInMemory(GeometryAsset geometryAsset) -> ulong
    Exact size measured from geometryAsset buffers (attrData.Length + indexData.Length).

  • (Private) CompleteCaching(Entity entity), CompleteCaching(Entity, CacheInfo)
    Helpers that Complete() the cache job and playback the recorded EntityCommandBuffer; enqueue geometry asset for unloading if applicable.

  • Several nested types:

  • LoadingData: simple (priority, batchIndex) with CompareTo to sort by descending priority.
  • MeshInfo: stores per-entity mesh meta (prefab, shape allocations, estimated size, generated index, count of batches).
  • CacheInfo: holds geometry asset, command buffer and dependency job handle for pending cache/un-cache operations.
  • ShapeAllocation: describes allocation in the heap (NativeHeapBlock), stride, and extents used to set shader parameters.
  • LoadingPriorityJob (Burst compiled IJob): updates priority/state lists each frame, moves batches between Pending/None/Obsolete/Complete and orders m_LoadingData / m_UnloadingData.

Usage Example

// Typical usage inside a mod/system initialization:
var batchMeshSystem = World.GetOrCreateSystemManaged<Game.Rendering.BatchMeshSystem>();

// Example: change memory budget and enable strict budget.
batchMeshSystem.memoryBudget = 1024UL * 1024UL * 1024UL; // 1 GiB
batchMeshSystem.strictMemoryBudget = true;

// Scheduling: if you have jobs that compute priorities, write into batchMeshSystem.GetBatchPriority(...)
// followed by calling batchMeshSystem.AddBatchPriorityWriter(myJobHandle) so the system knows about dependencies.

// System OnCreate is preserved/overridden internally. Example override in a derived system:
[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // e.g. disable dynamic mesh loading if you want to keep everything resident (not recommended)
    var batchMesh = World.GetOrCreateSystemManaged<Game.Rendering.BatchMeshSystem>();
    batchMesh.enableMeshLoading = true;
}

Notes and best practices: - When accessing the NativeList returned by GetBatchPriority/GetLoadingState you must respect the returned JobHandle (dependencies) to avoid race conditions: wait on the handle (or combine/chain dependencies) as appropriate. - The system schedules a Burst IJob (LoadingPriorityJob) to manage per-batch queues; set enableMeshLoading = false to disable it. - Generated Mesh instances are created and later finalized by calling GenerateMeshes() + CompleteMeshes(); the system coordinates these when running normally. - The shape buffer stores data in 8-byte (ulong) elements. Shape allocations are tracked by ShapeAllocation and referenced by material property blocks (two Vector4s written by SetShapeParameters). - Always allow the system to complete outstanding dependencies before destroying world/systems (OnDestroy completes JobHandles and disposes native memory). - Avoid manipulating internal collections (m_MeshInfos, etc.) directly; use the BatchManager/CustomBatch APIs and BatchMeshSystem public methods.

If you need documentation for any specific method, nested type, or the LoadingPriorityJob behavior in more detail, tell me which part and I'll expand with parameters, return values and examples.