Skip to content

Game.ProceduralSkeletonSystem

Assembly: Assembly-CSharp
Namespace: Game.Rendering

Type: class ProceduralSkeletonSystem

Base: GameSystemBase, IPreDeserialize

Summary:
ProceduralSkeletonSystem manages GPU-side storage and upload of procedural skeleton (bone) matrices used for skinned/procedural animation in the rendering pipeline. It owns a NativeHeapAllocator that allocates blocks of float4x4 matrices, a SparseUploader (and its threaded variant) to stream data to a GraphicsBuffer, and logic to retire/release allocations after a time window. The system also handles motion-vector (history) support by maintaining previous-frame bone matrices and coordinating buffer replacement when motion vector settings change. It schedules a Burst-compiled job (RemoveAllocationsJob) to release aged allocations from the native heap.


Fields

  • public struct AllocationInfo
    Container struct used in a NativeReference to track the number of active allocations (m_AllocationCount).

  • public struct AllocationRemove
    Used to enqueue allocations that should be released later. Contains:

  • NativeHeapBlock m_Allocation
  • int m_RemoveTime

  • [BurstCompile] private struct RemoveAllocationsJob : IJob
    Burst job that processes m_AllocationRemoves queue, releasing heap blocks whose age has exceeded the threshold and decrementing m_AllocationInfo.m_AllocationCount.

  • public const uint SKELETON_MEMORY_DEFAULT
    Default heap memory size in bytes for skeletons (4194304).

  • public const uint SKELETON_MEMORY_INCREMENT
    Increment size for skeleton memory growth (1048576).

  • public const uint UPLOADER_CHUNK_SIZE
    Chunk size used by the SparseUploader (524288).

  • private RenderingSystem m_RenderingSystem
    Reference to the game's RenderingSystem (used for motion vector and LOD timing info).

  • private NativeHeapAllocator m_HeapAllocator
    Native heap allocator that allocates blocks of float4x4 matrices for skeletons.

  • private SparseUploader m_SparseUploader
    Uploader that handles GPU-side sparse updates to the GraphicsBuffer.

  • private ThreadedSparseUploader m_ThreadedSparseUploader
    Threaded handle returned by the SparseUploader when beginning an upload.

  • private NativeReference<AllocationInfo> m_AllocationInfo
    NativeReference used to store allocation counts and shared between jobs.

  • private NativeQueue<AllocationRemove> m_AllocationRemoves
    Queue of allocations scheduled for deferred removal.

  • private bool m_IsAllocating
    Flag set when allocations were requested (used to ensure buffer updates).

  • private bool m_IsUploading
    Flag set while an upload is in progress.

  • private GraphicsBuffer m_ComputeBuffer
    The GPU buffer used to hold bone transform data; set as a global shader buffer (_BoneTransforms).

  • private JobHandle m_HeapDeps
    JobHandle for dependencies that write to the heap (used to synchronize heap access).

  • private JobHandle m_UploadDeps
    JobHandle for dependencies that perform upload work (used to synchronize uploads).

  • private int m_HeapAllocatorByteSize
    Cached byte-size of the heap allocator (in bytes, based on allocator.Size * sizeof(float4x4)).

  • private int m_CurrentTime
    Current time counter (used for timed release of allocations).

  • private bool m_AreMotionVectorsEnabled
    Cached value of whether motion vectors were enabled last update.

  • private bool m_ForceHistoryUpdate
    True when the history (previous transforms) must be updated/recreated due to changes in motion-vector setting.

  • public bool isMotionBlurEnabled => m_AreMotionVectorsEnabled
    Read-only property exposing whether motion vectors (motion blur) are enabled (cached).

  • public bool forceHistoryUpdate => m_ForceHistoryUpdate
    Read-only property exposing whether a forced history (previous transforms) update is pending.

Properties

  • public Unity.Jobs.JobHandle producerHandle { get; private set }
    (Not present in this type; included in the template example. ProceduralSkeletonSystem exposes job handles through methods/fields such as m_HeapDeps and m_UploadDeps rather than a producerHandle property.)

Constructors

  • public ProceduralSkeletonSystem()
    Default constructor. The class is also annotated with [Preserve] on lifecycle methods. The system performs its core initialization in OnCreate rather than in the constructor.

Methods

  • [Preserve] protected unsafe override void OnCreate() : System.Void
    Initializes internal resources:
  • Obtains RenderingSystem reference.
  • Creates NativeHeapAllocator with default size (SKELETON_MEMORY_DEFAULT / sizeof(float4x4)).
  • Creates a SparseUploader for streaming bone data to the GPU.
  • Creates NativeReference and NativeQueue.
  • Calls AllocateIdentityEntry() to reserve the first (identity) matrix entry. Note: method is unsafe due to interactions with low-level native memory.

  • [Preserve] protected override void OnDestroy() : System.Void
    Cleans up resources:

  • Completes outstanding uploads and heap jobs.
  • Disposes NativeHeapAllocator, SparseUploader, NativeReference and NativeQueue if created.
  • Releases the GraphicsBuffer if it exists.
  • Calls base.OnDestroy().

  • [Preserve] protected unsafe override void OnUpdate() : System.Void
    Main update loop:

  • Detects motion vector setting changes and toggles m_ForceHistoryUpdate accordingly.
  • Completes pending uploads and heap dependency jobs as needed.
  • If allocations are pending or history must be updated, recreates or resizes the GPU GraphicsBuffer (_BoneTransforms) to match heap size × (1 or 2) depending on motion-vector support and coordinates copying previous data where applicable.
  • Sets global shader int "_BonePreviousTransformsByteOffset".
  • If there are queued allocation removals, schedules the Burst RemoveAllocationsJob to retire aged allocations.

  • public ThreadedSparseUploader BeginUpload(int opCount, uint dataSize, uint maxOpSize, out int historyByteOffset)
    Begins a sparse uploader session:

  • Calls m_SparseUploader.Begin to prepare the threaded uploader.
  • Sets m_IsUploading = true.
  • Outputs historyByteOffset which is the byte offset of previous-frame transforms (m_HeapAllocatorByteSize).
  • Returns the ThreadedSparseUploader to be used by worker threads to write upload ops.

  • public void AddUploadWriter(JobHandle handle)
    Adds a JobHandle dependency for upload writers; stored in m_UploadDeps and later waited on during CompleteUpload.

  • public void CompleteUpload()
    Completes any active upload sequence:

  • If m_IsUploading is true, waits on m_UploadDeps, clears m_IsUploading, and calls m_SparseUploader.EndAndCommit with the threaded uploader to finalize GPU uploads.

  • public void PreDeserialize(Context context)
    IPreDeserialize implementation used before deserialization:

  • Completes heap jobs, clears the heap allocator and allocation removal queue, and re-allocates the identity entry. This ensures a clean allocator state before loading serialized data.

  • public NativeHeapAllocator GetHeapAllocator(out NativeReference<AllocationInfo> allocationInfo, out NativeQueue<AllocationRemove> allocationRemoves, out int currentTime, out JobHandle dependencies)
    Provides callers access to the NativeHeapAllocator and related synchronization primitives:

  • Returns m_HeapAllocator.
  • Outputs m_AllocationInfo, m_AllocationRemoves, m_CurrentTime and current m_HeapDeps JobHandle as dependencies.
  • Sets m_IsAllocating = true to indicate allocations are expected/occurred.

  • public void AddHeapWriter(JobHandle handle)
    Stores a JobHandle for a writer that modified the heap (m_HeapDeps) so the system can wait on it before performing operations that must see completed heap writes.

  • public unsafe void GetMemoryStats(out uint allocatedSize, out uint bufferSize, out uint currentUpload, out uint uploadSize, out int allocationCount)
    Gathers runtime memory statistics:

  • Completes m_HeapDeps to get consistent stats.
  • Computes allocatedSize (used heap space × sizeof(float4x4) × (1 or 2 depending on motion vectors)).
  • Computes bufferSize (allocator total size × sizeof(float4x4) × (1 or 2)).
  • Returns allocationCount from m_AllocationInfo.
  • Queries SparseUploader.ComputeStats() to return currentUpload (bytes uploaded this frame) and uploadSize (total bytes used by uploader).

  • private void AllocateIdentityEntry()
    Ensures an identity transform entry is present at index 0:

  • Sets m_IsAllocating = true, allocates a single matrix in the heap, and initializes m_AllocationInfo.Value.m_AllocationCount to 0.
  • This reserved identity entry is used when a skeleton has no bones or needs an identity fallback.

  • public static void GetSkinMatrices(Skeleton skeleton, in DynamicBuffer<ProceduralBone> proceduralBones, in DynamicBuffer<Bone> bones, NativeList<float4x4> tempMatrices)
    Helper that computes model-space and skin matrices for a procedural skeleton:

  • Iterates proceduralBones, computes local TRS matrix from Bone data, multiplies by parent matrix if parent exists, writes the result into tempMatrices at index i.
  • Also computes final skin matrix by multiplying model-space transform by the bone's bind pose and stores at tempMatrices[proceduralBones.Length + proceduralBone.m_BindIndex].
  • Useful for preparing the float4x4 arrays that will be uploaded to the GPU.

Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // Example: Begin a threaded sparse upload for bone data.
    int historyByteOffset;
    // opCount: number of GPU upload operations, dataSize: total bytes to upload, maxOpSize: max bytes per op
    var threadedUploader = ProceduralSkeletonSystemInstance.BeginUpload(opCount: 1, dataSize: 1024, maxOpSize: 1024, out historyByteOffset);

    // Worker jobs can write upload ops into threadedUploader here.
    // When those jobs are scheduled, register their JobHandle:
    // ProceduralSkeletonSystemInstance.AddUploadWriter(myJobHandle);

    // After writers complete, finalize:
    // ProceduralSkeletonSystemInstance.CompleteUpload();
}

Notes and tips: - Always call AddHeapWriter/AddUploadWriter with the JobHandle of any job that writes to the heap or upload buffers so the system can synchronize before resizing or committing. - Use GetHeapAllocator to obtain the allocator and the allocation queues when performing multi-threaded allocation; respect the returned dependencies JobHandle. - When motion vectors are enabled the system maintains two sets of transforms (current and previous). The historyByteOffset returned by BeginUpload can be used when writing data into the GPU buffer to place previous-frame transforms at the correct byte offset. - PreDeserialize is invoked to reset the allocator state before loading serialized data; do not rely on heap contents across a deserialize call.