A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using Unity.Collections;
4using Unity.Collections.LowLevel.Unsafe;
5using Unity.Jobs;
6using Unity.Mathematics;
7using Unity.Profiling;
8using UnityEngine.U2D.Common;
9
10namespace UnityEngine.U2D.Animation
11{
12 internal abstract class BaseDeformationSystem
13 {
14 protected static class Profiling
15 {
16 public static readonly ProfilerMarker transformAccessJob = new ProfilerMarker("BaseDeformationSystem.TransformAccessJob");
17 public static readonly ProfilerMarker getSpriteSkinBatchData = new ProfilerMarker("BaseDeformationSystem.GetSpriteSkinBatchData");
18 public static readonly ProfilerMarker scheduleJobs = new ProfilerMarker("BaseDeformationSystem.ScheduleJobs");
19 public static readonly ProfilerMarker setBatchDeformableBufferAndLocalAABB = new ProfilerMarker("BaseDeformationSystem.SetBatchDeformableBufferAndLocalAABB");
20 public static readonly ProfilerMarker setBoneTransformsArray = new ProfilerMarker("BaseDeformationSystem.SetBoneTransformsArray");
21 }
22
23 public abstract DeformationMethods deformationMethod { get; }
24
25 protected int m_ObjectId;
26
27 protected readonly HashSet<SpriteSkin> m_SpriteSkins = new HashSet<SpriteSkin>();
28 protected SpriteRenderer[] m_SpriteRenderers = new SpriteRenderer[0];
29
30 readonly HashSet<SpriteSkin> m_SpriteSkinsToAdd = new HashSet<SpriteSkin>();
31 readonly HashSet<SpriteSkin> m_SpriteSkinsToRemove = new HashSet<SpriteSkin>();
32 readonly List<int> m_TransformIdsToRemove = new List<int>();
33
34 protected NativeByteArray m_DeformedVerticesBuffer;
35 protected NativeArray<float4x4> m_FinalBoneTransforms;
36
37 protected NativeArray<bool> m_IsSpriteSkinActiveForDeform;
38 protected NativeArray<SpriteSkinData> m_SpriteSkinData;
39 protected NativeArray<PerSkinJobData> m_PerSkinJobData;
40 protected NativeArray<Bounds> m_BoundsData;
41 protected NativeArray<IntPtr> m_Buffers;
42 protected NativeArray<int> m_BufferSizes;
43 protected NativeArray<IntPtr> m_BoneTransformBuffers;
44
45 protected NativeArray<int2> m_BoneLookupData;
46 protected NativeArray<int2> m_VertexLookupData;
47 protected NativeArray<PerSkinJobData> m_SkinBatchArray;
48
49 TransformAccessJob m_LocalToWorldTransformAccessJob;
50 TransformAccessJob m_WorldToLocalTransformAccessJob;
51
52 protected JobHandle m_DeformJobHandle;
53
54 internal void RemoveBoneTransforms(SpriteSkin spriteSkin)
55 {
56 if (!m_SpriteSkins.Contains(spriteSkin))
57 return;
58
59 m_LocalToWorldTransformAccessJob.RemoveTransformById(spriteSkin.rootBoneTransformId);
60 var boneTransforms = spriteSkin.boneTransformId;
61 if (boneTransforms == default || !boneTransforms.IsCreated)
62 return;
63
64 for (var i = 0; i < boneTransforms.Length; ++i)
65 m_LocalToWorldTransformAccessJob.RemoveTransformById(boneTransforms[i]);
66 }
67
68 internal void AddBoneTransforms(SpriteSkin spriteSkin)
69 {
70 if (!m_SpriteSkins.Contains(spriteSkin))
71 return;
72
73 m_LocalToWorldTransformAccessJob.AddTransform(spriteSkin.rootBone);
74 if (spriteSkin.boneTransforms != null)
75 {
76 foreach (var t in spriteSkin.boneTransforms)
77 {
78 if (t != null)
79 m_LocalToWorldTransformAccessJob.AddTransform(t);
80 }
81 }
82 }
83
84 internal virtual void UpdateMaterial(SpriteSkin spriteSkin) { }
85
86 internal virtual bool AddSpriteSkin(SpriteSkin spriteSkin)
87 {
88 if (!m_SpriteSkins.Contains(spriteSkin) && !m_SpriteSkinsToAdd.Contains(spriteSkin))
89 {
90 m_SpriteSkinsToAdd.Add(spriteSkin);
91 return true;
92 }
93
94 if (m_SpriteSkinsToRemove.Contains(spriteSkin))
95 {
96 m_SpriteSkinsToAdd.Add(spriteSkin);
97 return true;
98 }
99
100 return false;
101 }
102
103 internal void CopyToSpriteSkinData(SpriteSkin spriteSkin)
104 {
105 if (!m_SpriteSkinData.IsCreated)
106 throw new InvalidOperationException("Sprite Skin Data not initialized.");
107
108 var dataIndex = spriteSkin.dataIndex;
109 if(dataIndex < 0 || dataIndex >= m_SpriteSkinData.Length)
110 return;
111
112 var spriteSkinData = default(SpriteSkinData);
113 spriteSkin.CopyToSpriteSkinData(ref spriteSkinData);
114
115 m_SpriteSkinData[dataIndex] = spriteSkinData;
116 m_SpriteRenderers[dataIndex] = spriteSkin.spriteRenderer;
117 }
118
119 internal void RemoveSpriteSkin(SpriteSkin spriteSkin)
120 {
121 if (spriteSkin == null)
122 return;
123
124 if (m_SpriteSkins.Contains(spriteSkin) && !m_SpriteSkinsToRemove.Contains(spriteSkin))
125 {
126 m_SpriteSkinsToRemove.Add(spriteSkin);
127 m_TransformIdsToRemove.Add(spriteSkin.transform.GetInstanceID());
128 }
129
130 if (m_SpriteSkinsToAdd.Contains(spriteSkin))
131 m_SpriteSkinsToAdd.Remove(spriteSkin);
132
133 RemoveBoneTransforms(spriteSkin);
134 }
135
136 internal HashSet<SpriteSkin> GetSpriteSkins()
137 {
138 return m_SpriteSkins;
139 }
140
141 internal void Initialize(int objectId)
142 {
143 m_ObjectId = objectId;
144
145 if (m_LocalToWorldTransformAccessJob == null)
146 m_LocalToWorldTransformAccessJob = new TransformAccessJob();
147 if (m_WorldToLocalTransformAccessJob == null)
148 m_WorldToLocalTransformAccessJob = new TransformAccessJob();
149
150 InitializeArrays();
151 BatchRemoveSpriteSkins();
152 BatchAddSpriteSkins();
153
154 // Initialise all existing SpriteSkins as execution order is indeterminate
155 var count = 0;
156 foreach (var spriteSkin in m_SpriteSkins)
157 {
158 spriteSkin.SetDataIndex(count++);
159
160 CopyToSpriteSkinData(spriteSkin);
161 }
162 }
163
164 protected virtual void InitializeArrays()
165 {
166 const int startingCount = 0;
167
168 m_FinalBoneTransforms = new NativeArray<float4x4>(startingCount, Allocator.Persistent);
169 m_BoneLookupData = new NativeArray<int2>(startingCount, Allocator.Persistent);
170 m_VertexLookupData = new NativeArray<int2>(startingCount, Allocator.Persistent);
171 m_SkinBatchArray = new NativeArray<PerSkinJobData>(startingCount, Allocator.Persistent);
172
173 m_IsSpriteSkinActiveForDeform = new NativeArray<bool>(startingCount, Allocator.Persistent);
174 m_PerSkinJobData = new NativeArray<PerSkinJobData>(startingCount, Allocator.Persistent);
175 m_SpriteSkinData = new NativeArray<SpriteSkinData>(startingCount, Allocator.Persistent);
176 m_BoundsData = new NativeArray<Bounds>(startingCount, Allocator.Persistent);
177 m_Buffers = new NativeArray<IntPtr>(startingCount, Allocator.Persistent);
178 m_BufferSizes = new NativeArray<int>(startingCount, Allocator.Persistent);
179 }
180
181 protected void BatchRemoveSpriteSkins()
182 {
183 var spritesToRemoveCount = m_SpriteSkinsToRemove.Count;
184 if (spritesToRemoveCount == 0)
185 return;
186
187 m_WorldToLocalTransformAccessJob.RemoveTransformsByIds(m_TransformIdsToRemove);
188
189 var updatedCount = Math.Max(m_SpriteSkins.Count - spritesToRemoveCount, 0);
190 if (updatedCount == 0)
191 {
192 m_SpriteSkins.Clear();
193 }
194 else
195 {
196 foreach (var spriteSkin in m_SpriteSkinsToRemove)
197 m_SpriteSkins.Remove(spriteSkin);
198 }
199
200 var count = 0;
201 foreach (var spriteSkin in m_SpriteSkins)
202 {
203 spriteSkin.SetDataIndex(count++);
204 CopyToSpriteSkinData(spriteSkin);
205 }
206
207 Array.Resize(ref m_SpriteRenderers, updatedCount);
208 ResizeAndCopyArrays(updatedCount);
209
210 m_TransformIdsToRemove.Clear();
211 m_SpriteSkinsToRemove.Clear();
212 }
213
214 protected void BatchAddSpriteSkins()
215 {
216 if (m_SpriteSkinsToAdd.Count == 0)
217 return;
218
219 if (!m_IsSpriteSkinActiveForDeform.IsCreated)
220 throw new InvalidOperationException("SpriteSkinActiveForDeform not initialized.");
221
222 var updatedCount = m_SpriteSkins.Count + m_SpriteSkinsToAdd.Count;
223 Array.Resize(ref m_SpriteRenderers, updatedCount);
224 ResizeAndCopyArrays(updatedCount);
225
226 foreach (var spriteSkin in m_SpriteSkinsToAdd)
227 {
228 if (m_SpriteSkins.Contains(spriteSkin))
229 {
230 Debug.LogError($"Skin already exists! Name={spriteSkin.name}");
231 continue;
232 }
233
234 m_SpriteSkins.Add(spriteSkin);
235 UpdateMaterial(spriteSkin);
236 var count = m_SpriteSkins.Count;
237
238 m_SpriteRenderers[count - 1] = spriteSkin.spriteRenderer;
239 m_WorldToLocalTransformAccessJob.AddTransform(spriteSkin.transform);
240
241 AddBoneTransforms(spriteSkin);
242
243 spriteSkin.SetDataIndex(count - 1);
244 CopyToSpriteSkinData(spriteSkin);
245 }
246
247 m_SpriteSkinsToAdd.Clear();
248 }
249
250 protected virtual void ResizeAndCopyArrays(int updatedCount)
251 {
252 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_IsSpriteSkinActiveForDeform, updatedCount);
253 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_PerSkinJobData, updatedCount);
254 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_Buffers, updatedCount);
255 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BufferSizes, updatedCount);
256 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_SpriteSkinData, updatedCount);
257 NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BoundsData, updatedCount);
258 }
259
260 internal virtual void Cleanup()
261 {
262 m_DeformJobHandle.Complete();
263
264 m_SpriteSkins.Clear();
265 m_SpriteRenderers = new SpriteRenderer[0];
266 BufferManager.instance.ReturnBuffer(m_ObjectId);
267 m_IsSpriteSkinActiveForDeform.DisposeIfCreated();
268 m_PerSkinJobData.DisposeIfCreated();
269 m_Buffers.DisposeIfCreated();
270 m_BufferSizes.DisposeIfCreated();
271 m_SpriteSkinData.DisposeIfCreated();
272 m_BoneLookupData.DisposeIfCreated();
273 m_VertexLookupData.DisposeIfCreated();
274 m_SkinBatchArray.DisposeIfCreated();
275 m_FinalBoneTransforms.DisposeIfCreated();
276 m_BoundsData.DisposeIfCreated();
277
278 m_LocalToWorldTransformAccessJob.Destroy();
279 m_WorldToLocalTransformAccessJob.Destroy();
280 }
281
282 internal abstract void Update();
283
284 protected void PrepareDataForDeformation(out JobHandle localToWorldJobHandle, out JobHandle worldToLocalJobHandle)
285 {
286 ValidateSpriteSkinData();
287
288 using (Profiling.transformAccessJob.Auto())
289 {
290 localToWorldJobHandle = m_LocalToWorldTransformAccessJob.StartLocalToWorldJob();
291 worldToLocalJobHandle = m_WorldToLocalTransformAccessJob.StartWorldToLocalJob();
292 }
293
294 using (Profiling.getSpriteSkinBatchData.Auto())
295 {
296 NativeArrayHelpers.ResizeIfNeeded(ref m_SkinBatchArray, 1);
297 var fillPerSkinJobSingleThread = new FillPerSkinJobSingleThread()
298 {
299 isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform,
300 combinedSkinBatchArray = m_SkinBatchArray,
301 spriteSkinDataArray = m_SpriteSkinData,
302 perSkinJobDataArray = m_PerSkinJobData,
303 };
304 fillPerSkinJobSingleThread.Run();
305 }
306 }
307
308 void ValidateSpriteSkinData()
309 {
310 foreach (var spriteSkin in m_SpriteSkins)
311 {
312 var index = spriteSkin.dataIndex;
313 m_IsSpriteSkinActiveForDeform[index] = spriteSkin.BatchValidate();
314 if (m_IsSpriteSkinActiveForDeform[index] && spriteSkin.NeedToUpdateDeformationCache())
315 CopyToSpriteSkinData(spriteSkin);
316 }
317 }
318
319 protected bool GotVerticesToDeform(out int vertexBufferSize)
320 {
321 vertexBufferSize = m_SkinBatchArray[0].deformVerticesStartPos;
322 return vertexBufferSize > 0;
323 }
324
325 protected JobHandle SchedulePrepareJob(int batchCount)
326 {
327 var prepareJob = new PrepareDeformJob
328 {
329 batchDataSize = batchCount,
330 perSkinJobData = m_PerSkinJobData,
331 boneLookupData = m_BoneLookupData,
332 vertexLookupData = m_VertexLookupData
333 };
334 return prepareJob.Schedule();
335 }
336
337 protected JobHandle ScheduleBoneJobBatched(JobHandle jobHandle, PerSkinJobData skinBatch)
338 {
339 var boneJobBatched = new BoneDeformBatchedJob()
340 {
341 boneTransform = m_LocalToWorldTransformAccessJob.transformMatrix,
342 rootTransform = m_WorldToLocalTransformAccessJob.transformMatrix,
343 spriteSkinData = m_SpriteSkinData,
344 boneLookupData = m_BoneLookupData,
345 finalBoneTransforms = m_FinalBoneTransforms,
346 rootTransformIndex = m_WorldToLocalTransformAccessJob.transformData,
347 boneTransformIndex = m_LocalToWorldTransformAccessJob.transformData
348 };
349 jobHandle = boneJobBatched.Schedule(skinBatch.bindPosesIndex.y, 8, jobHandle);
350 return jobHandle;
351 }
352
353 protected JobHandle ScheduleSkinDeformBatchedJob(JobHandle jobHandle, PerSkinJobData skinBatch)
354 {
355 var skinJobBatched = new SkinDeformBatchedJob()
356 {
357 vertices = m_DeformedVerticesBuffer.array,
358 vertexLookupData = m_VertexLookupData,
359 spriteSkinData = m_SpriteSkinData,
360 perSkinJobData = m_PerSkinJobData,
361 finalBoneTransforms = m_FinalBoneTransforms,
362 };
363 return skinJobBatched.Schedule(skinBatch.verticesIndex.y, 16, jobHandle);
364 }
365
366 protected unsafe JobHandle ScheduleCopySpriteRendererBuffersJob(JobHandle jobHandle, int batchCount)
367 {
368 var copySpriteRendererBuffersJob = new CopySpriteRendererBuffersJob()
369 {
370 isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform,
371 spriteSkinData = m_SpriteSkinData,
372 ptrVertices = (IntPtr)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_DeformedVerticesBuffer.array),
373 buffers = m_Buffers,
374 bufferSizes = m_BufferSizes,
375 };
376 return copySpriteRendererBuffersJob.Schedule(batchCount, 16, jobHandle);
377 }
378
379 protected JobHandle ScheduleCalculateSpriteSkinAABBJob(JobHandle jobHandle, int batchCount)
380 {
381 var updateBoundJob = new CalculateSpriteSkinAABBJob
382 {
383 vertices = m_DeformedVerticesBuffer.array,
384 isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform,
385 spriteSkinData = m_SpriteSkinData,
386 bounds = m_BoundsData,
387 };
388 return updateBoundJob.Schedule(batchCount, 4, jobHandle);
389 }
390
391 protected void DeactivateDeformableBuffers()
392 {
393 for (var i = 0; i < m_IsSpriteSkinActiveForDeform.Length; ++i)
394 {
395 if (m_IsSpriteSkinActiveForDeform[i] || InternalEngineBridge.IsUsingDeformableBuffer(m_SpriteRenderers[i], IntPtr.Zero))
396 continue;
397 m_SpriteRenderers[i].DeactivateDeformableBuffer();
398 }
399 }
400
401 internal bool IsSpriteSkinActiveForDeformation(SpriteSkin spriteSkin)
402 {
403 return m_IsSpriteSkinActiveForDeform[spriteSkin.dataIndex];
404 }
405
406 internal unsafe NativeArray<byte> GetDeformableBufferForSpriteSkin(SpriteSkin spriteSkin)
407 {
408 if (!m_SpriteSkins.Contains(spriteSkin))
409 return default;
410
411 if (!m_DeformJobHandle.IsCompleted)
412 m_DeformJobHandle.Complete();
413
414 var skinData = m_SpriteSkinData[spriteSkin.dataIndex];
415 if (skinData.deformVerticesStartPos < 0)
416 return default;
417
418 var vertexBufferLength = skinData.spriteVertexCount * skinData.spriteVertexStreamSize;
419 var ptrVertices = (byte*)m_DeformedVerticesBuffer.array.GetUnsafeReadOnlyPtr();
420 ptrVertices += skinData.deformVerticesStartPos;
421 var buffer = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(ptrVertices, vertexBufferLength, Allocator.None);
422#if ENABLE_UNITY_COLLECTIONS_CHECKS
423 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref buffer, NativeArrayUnsafeUtility.GetAtomicSafetyHandle(m_DeformedVerticesBuffer.array));
424#endif
425 return buffer;
426 }
427
428#if UNITY_INCLUDE_TESTS
429 internal TransformAccessJob GetWorldToLocalTransformAccessJob() => m_WorldToLocalTransformAccessJob;
430 internal TransformAccessJob GetLocalToWorldTransformAccessJob() => m_LocalToWorldTransformAccessJob;
431#endif
432 }
433}