-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathAvatarTransformMatrixJobWrapper.cs
More file actions
146 lines (119 loc) · 5.99 KB
/
Copy pathAvatarTransformMatrixJobWrapper.cs
File metadata and controls
146 lines (119 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System;
using DCL.AvatarRendering.AvatarShape.ComputeShader;
using DCL.AvatarRendering.AvatarShape.UnityInterface;
using DCL.Utility;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
namespace DCL.AvatarRendering.AvatarShape.Components
{
public class AvatarTransformMatrixJobWrapper : IDisposable
{
// Each task processes one full avatar (62 bone multiplies). Small batch count keeps
// worker utilisation high without excessive scheduling overhead.
private const int BONE_MATRIX_BATCH_COUNT = 4;
internal const int AVATAR_ARRAY_SIZE = 100;
private const int BONES_ARRAY_LENGTH = ComputeShaderConstants.MAX_BONE_COUNT;
private const int BONES_PER_AVATAR_LENGTH = AVATAR_ARRAY_SIZE * BONES_ARRAY_LENGTH;
private bool disposed;
// Placeholder transform for released or unassigned slots in the TAAs.
private readonly Transform dummyTransform;
private readonly MainPlayerPipeline mainPlayerAvatar;
private readonly RemoteAvatarPipeline remoteAvatars;
public NativeArray<float4x4> MainPlayerBonesResult => mainPlayerAvatar.Job.BonesMatricesResult;
public NativeArray<float4x4> RemoteAvatarsBonesResult => remoteAvatars.Job.BonesMatricesResult;
#if UNITY_INCLUDE_TESTS
public int MatrixFromAllAvatarsLength => remoteAvatars.MatrixFromAllAvatarsLength;
public int UpdateAvatarLength => remoteAvatars.UpdateAvatarLength;
public int CurrentAvatarAmountSupported => remoteAvatars.CurrentAvatarAmountSupported;
#endif
public AvatarTransformMatrixJobWrapper()
{
var dummyGO = new GameObject("AvatarTransformMatrixDummy") { hideFlags = HideFlags.HideAndDontSave };
dummyTransform = dummyGO.transform;
remoteAvatars = new RemoteAvatarPipeline(AVATAR_ARRAY_SIZE, BONES_ARRAY_LENGTH, BONES_PER_AVATAR_LENGTH, dummyTransform);
mainPlayerAvatar = new MainPlayerPipeline(BONES_ARRAY_LENGTH);
}
/// <summary>
/// Schedules bone gather + matrix calculation for all avatars.
/// The main player pipeline is completed immediately so its transforms are unlocked
/// before InterpolateCharacterSystem runs.
/// </summary>
public void ScheduleBoneMatrixCalculation()
{
mainPlayerAvatar.ScheduleAndComplete();
remoteAvatars.Schedule(BONE_MATRIX_BATCH_COUNT);
}
public void CompleteBoneMatrixCalculations()
{
remoteAvatars.Complete();
}
/// <summary>
/// Registers the main player avatar into a dedicated pipeline whose transforms
/// are gathered and released before the remote batch, preventing TransformAccessArray
/// locks from blocking InterpolateCharacterSystem.
/// </summary>
/// <summary>
/// Registers from a local (pre-Add) component. Sets index and flag on the component
/// so the caller can pass it into World.Add already registered.
/// </summary>
public void RegisterMainPlayerAvatar(AvatarBase avatarBase, ref AvatarTransformMatrixComponent transformMatrixComponent)
{
transformMatrixComponent.IndexInGlobalJobArray = GlobalJobArrayIndex.ValidUnsafe(0);
transformMatrixComponent.IsMainPlayer = true;
mainPlayerAvatar.Register(avatarBase.transform, transformMatrixComponent.bones, dummyTransform);
}
/// <summary>
/// Registers a remote avatar for bone matrix calculation.
/// Subsequent calls for already-registered avatars are no-ops; per-frame work is handled by the gather jobs.
/// </summary>
public void RegisterAvatar(AvatarBase avatarBase, ref AvatarTransformMatrixComponent transformMatrixComponent)
{
remoteAvatars.Register(avatarBase, ref transformMatrixComponent);
}
/// <summary>
/// Pushes the avatar's authoritative bone count (AvatarCustomSkinningComponent.BoneCount — the
/// exact number of matrices ComputeSkinning uploads) into the matching pipeline so the
/// calculation job produces precisely that range. Must be called every frame before
/// ScheduleBoneMatrixCalculation; unregistered avatars (invalid index) are ignored.
/// </summary>
public void SetBoneCount(ref AvatarTransformMatrixComponent transformMatrixComponent, int boneCount)
{
if (transformMatrixComponent.IndexInGlobalJobArray.TryGetValue(out int validIndex) == false)
return;
if (transformMatrixComponent.IsMainPlayer)
mainPlayerAvatar.SetBoneCount(boneCount);
else
remoteAvatars.SetBoneCount(validIndex, boneCount);
}
public void Dispose()
{
// Leak the resouces. Managed dispose of TransformAccessArray takes very much time.
if (DCL.Utility.ExitUtils.IsAboutToQuit)
{
return;
}
var stopwatch = ShutdownStopwatch.StartNew(nameof(AvatarTransformMatrixJobWrapper));
remoteAvatars.Complete();
stopwatch.LogStep("remoteAvatars.Complete");
remoteAvatars.Dispose();
stopwatch.LogStep("remoteAvatars.Dispose");
mainPlayerAvatar.Dispose();
stopwatch.LogStep("mainPlayerAvatar.Dispose");
if (dummyTransform != null)
{
UnityEngine.Object.Destroy(dummyTransform.gameObject);
stopwatch.LogStep("dummyTransform.Destroy");
}
disposed = true;
}
public void ReleaseAvatar(ref AvatarTransformMatrixComponent avatarTransformMatrixComponent)
{
if (disposed) return;
//Main player avatar never gets released
if (avatarTransformMatrixComponent.IsMainPlayer)
return;
remoteAvatars.Release(ref avatarTransformMatrixComponent);
}
}
}