#if ENABLE_ANIMATION_COLLECTION using System; using Unity.Mathematics; using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.U2D.Common; using Unity.Burst; using Unity.Profiling; using UnityEngine.Assertions; namespace UnityEngine.U2D.Animation { internal struct PerSkinJobData { public int deformVerticesStartPos; public int2 bindPosesIndex; public int2 verticesIndex; } internal struct SpriteSkinData { public NativeCustomSlice vertices; public NativeCustomSlice boneWeights; public NativeCustomSlice bindPoses; public NativeCustomSlice tangents; public bool hasTangents; public int spriteVertexStreamSize; public int spriteVertexCount; public int tangentVertexOffset; public int deformVerticesStartPos; public int transformId; public NativeCustomSlice boneTransformId; } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct PrepareDeformJob :IJob { [ReadOnly] public NativeArray perSkinJobData; [ReadOnly] public int batchDataSize; [WriteOnly] public NativeArray boneLookupData; [WriteOnly] public NativeArray vertexLookupData; public void Execute() { for (var i = 0; i < batchDataSize; ++i) { var jobData = perSkinJobData[i]; for (int k = 0, j = jobData.bindPosesIndex.x; j < jobData.bindPosesIndex.y; ++j, ++k) { boneLookupData[j] = new int2(i, k); } for (int k = 0, j = jobData.verticesIndex.x; j < jobData.verticesIndex.y; ++j, ++k) { vertexLookupData[j] = new int2(i, k); } } } } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct BoneDeformBatchedJob : IJobParallelFor { [ReadOnly] public NativeArray boneTransform; [ReadOnly] public NativeArray rootTransform; [ReadOnly] public NativeArray boneLookupData; [ReadOnly] public NativeArray spriteSkinData; [ReadOnly] public NativeHashMap rootTransformIndex; [ReadOnly] public NativeHashMap boneTransformIndex; [WriteOnly] public NativeArray finalBoneTransforms; public void Execute(int i) { var x = boneLookupData[i].x; var y = boneLookupData[i].y; var ssd = spriteSkinData[x]; var v = ssd.boneTransformId[y]; var index = boneTransformIndex[v].transformIndex; if (index < 0) return; var aa = boneTransform[index]; var bb = ssd.bindPoses[y]; var cc = rootTransformIndex[ssd.transformId].transformIndex; finalBoneTransforms[i] = math.mul(rootTransform[cc], math.mul(aa, bb)); } } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct SkinDeformBatchedJob : IJobParallelFor { public NativeSlice vertices; [ReadOnly] public NativeArray finalBoneTransforms; [ReadOnly] public NativeArray perSkinJobData; [ReadOnly] public NativeArray spriteSkinData; [ReadOnly] public NativeArray vertexLookupData; public unsafe void Execute(int i) { var j = vertexLookupData[i].x; var k = vertexLookupData[i].y; var perSkinData = perSkinJobData[j]; var spriteSkin = spriteSkinData[j]; var srcVertex = (float3)spriteSkin.vertices[k]; var tangents = (float4)spriteSkin.tangents[k]; var influence = spriteSkin.boneWeights[k]; var bone0 = influence.boneIndex0 + perSkinData.bindPosesIndex.x; var bone1 = influence.boneIndex1 + perSkinData.bindPosesIndex.x; var bone2 = influence.boneIndex2 + perSkinData.bindPosesIndex.x; var bone3 = influence.boneIndex3 + perSkinData.bindPosesIndex.x; var deformedPosOffset = (byte*)vertices.GetUnsafePtr(); var deformedPosStart = deformedPosOffset + spriteSkin.deformVerticesStartPos; var deformableVerticesFloat3 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice(deformedPosStart, spriteSkin.spriteVertexStreamSize, spriteSkin.spriteVertexCount); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref deformableVerticesFloat3, NativeSliceUnsafeUtility.GetAtomicSafetyHandle(vertices)); #endif if (spriteSkin.hasTangents) { var deformedTanOffset = deformedPosStart + spriteSkin.tangentVertexOffset; var deformableTangentsFloat4 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice(deformedTanOffset, spriteSkin.spriteVertexStreamSize, spriteSkin.spriteVertexCount); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref deformableTangentsFloat4, NativeSliceUnsafeUtility.GetAtomicSafetyHandle(vertices)); #endif var tangent = new float4(tangents.xyz, 0.0f); tangent = math.mul(finalBoneTransforms[bone0], tangent) * influence.weight0 + math.mul(finalBoneTransforms[bone1], tangent) * influence.weight1 + math.mul(finalBoneTransforms[bone2], tangent) * influence.weight2 + math.mul(finalBoneTransforms[bone3], tangent) * influence.weight3; deformableTangentsFloat4[k] = new float4(math.normalize(tangent.xyz), tangents.w); } deformableVerticesFloat3[k] = math.transform(finalBoneTransforms[bone0], srcVertex) * influence.weight0 + math.transform(finalBoneTransforms[bone1], srcVertex) * influence.weight1 + math.transform(finalBoneTransforms[bone2], srcVertex) * influence.weight2 + math.transform(finalBoneTransforms[bone3], srcVertex) * influence.weight3; } } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct CalculateSpriteSkinAABBJob : IJobParallelFor { public NativeSlice vertices; [ReadOnly] public NativeArray isSpriteSkinValidForDeformArray; [ReadOnly] public NativeArray spriteSkinData; [WriteOnly] public NativeArray bounds; public unsafe void Execute(int i) { if (!isSpriteSkinValidForDeformArray[i]) return; var spriteSkin = spriteSkinData[i]; var deformedPosOffset = (byte*)vertices.GetUnsafePtr(); var deformableVerticesFloat3 = NativeSliceUnsafeUtility.ConvertExistingDataToNativeSlice(deformedPosOffset + spriteSkin.deformVerticesStartPos, spriteSkin.spriteVertexStreamSize, spriteSkin.spriteVertexCount); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeSliceUnsafeUtility.SetAtomicSafetyHandle(ref deformableVerticesFloat3, NativeSliceUnsafeUtility.GetAtomicSafetyHandle(vertices)); #endif bounds[i] = SpriteSkinUtility.CalculateSpriteSkinBounds(deformableVerticesFloat3); } } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct FillPerSkinJobSingleThread : IJob { public PerSkinJobData combinedSkinBatch; [ReadOnly] public NativeArray isSpriteSkinValidForDeformArray; public NativeArray spriteSkinDataArray; public NativeArray perSkinJobDataArray; public NativeArray combinedSkinBatchArray; public void Execute() { var startIndex = 0; var endIndex = spriteSkinDataArray.Length; for (var index = startIndex; index < endIndex; ++index) { var spriteSkinData = spriteSkinDataArray[index]; spriteSkinData.deformVerticesStartPos = -1; var vertexBufferSize = 0; var vertexCount = 0; var bindPoseCount = 0; if (isSpriteSkinValidForDeformArray[index]) { spriteSkinData.deformVerticesStartPos = combinedSkinBatch.deformVerticesStartPos; vertexBufferSize = spriteSkinData.spriteVertexCount * spriteSkinData.spriteVertexStreamSize; vertexCount = spriteSkinData.spriteVertexCount; bindPoseCount = spriteSkinData.bindPoses.Length; } combinedSkinBatch.verticesIndex.x = combinedSkinBatch.verticesIndex.y; combinedSkinBatch.verticesIndex.y = combinedSkinBatch.verticesIndex.x + vertexCount; combinedSkinBatch.bindPosesIndex.x = combinedSkinBatch.bindPosesIndex.y; combinedSkinBatch.bindPosesIndex.y = combinedSkinBatch.bindPosesIndex.x + bindPoseCount; spriteSkinDataArray[index] = spriteSkinData; perSkinJobDataArray[index] = combinedSkinBatch; combinedSkinBatch.deformVerticesStartPos += vertexBufferSize; } combinedSkinBatchArray[0] = combinedSkinBatch; } } #if ENABLE_ANIMATION_BURST [BurstCompile] #endif internal struct CopySpriteRendererBuffersJob : IJobParallelFor { [ReadOnly] public NativeArray isSpriteSkinValidForDeformArray; [ReadOnly] public NativeArray spriteSkinData; [ReadOnly, NativeDisableUnsafePtrRestriction] public IntPtr ptrVertices; [WriteOnly] public NativeArray buffers; [WriteOnly] public NativeArray bufferSizes; public void Execute(int i) { var skinData = spriteSkinData[i]; var startVertices = default(IntPtr); var vertexBufferLength = 0; if (isSpriteSkinValidForDeformArray[i]) { startVertices = ptrVertices + skinData.deformVerticesStartPos; vertexBufferLength = skinData.spriteVertexCount * skinData.spriteVertexStreamSize; } buffers[i] = startVertices; bufferSizes[i] = vertexBufferLength; } } internal class SpriteSkinComposite : ScriptableObject { static class Profiling { public static readonly ProfilerMarker prepareData = new ProfilerMarker("SpriteSkinComposite.PrepareData"); public static readonly ProfilerMarker validateSpriteSkinData = new ProfilerMarker("SpriteSkinComposite.ValidateSpriteSkinData"); public static readonly ProfilerMarker transformAccessJob = new ProfilerMarker("SpriteSkinComposite.TransformAccessJob"); public static readonly ProfilerMarker getSpriteSkinBatchData = new ProfilerMarker("SpriteSkinComposite.GetSpriteSkinBatchData"); public static readonly ProfilerMarker resizeBuffers = new ProfilerMarker("SpriteSkinComposite.ResizeBuffers"); public static readonly ProfilerMarker prepare = new ProfilerMarker("SpriteSkinComposite.Prepare"); public static readonly ProfilerMarker scheduleJobs = new ProfilerMarker("SpriteSkinComposite.ScheduleJobs"); public static readonly ProfilerMarker setBatchDeformableBufferAndLocalAABB = new ProfilerMarker("SpriteSkinComposite.SetBatchDeformableBufferAndLocalAABB"); public static readonly ProfilerMarker deactivateDeformableBuffer = new ProfilerMarker("SpriteSkinComposite.DeactivateDeformableBuffer"); } static SpriteSkinComposite s_Instance; public static SpriteSkinComposite instance { get { if (s_Instance == null) { var composite = Resources.FindObjectsOfTypeAll(); if (composite.Length > 0) s_Instance = composite[0]; else s_Instance = ScriptableObject.CreateInstance(); s_Instance.hideFlags = HideFlags.HideAndDontSave; s_Instance.Init(); } return s_Instance; } } List m_SpriteSkins = new List(); List m_SpriteSkinLateUpdate = new List(); SpriteRenderer[] m_SpriteRenderers = new SpriteRenderer[0]; NativeByteArray m_DeformedVerticesBuffer; NativeArray m_FinalBoneTransforms; NativeArray m_IsSpriteSkinActiveForDeform; NativeArray m_SpriteSkinData; NativeArray m_PerSkinJobData; NativeArray m_BoundsData; NativeArray m_Buffers; NativeArray m_BufferSizes; NativeArray m_BoneLookupData; NativeArray m_VertexLookupData; NativeArray m_SkinBatchArray; TransformAccessJob m_LocalToWorldTransformAccessJob; TransformAccessJob m_WorldToLocalTransformAccessJob; JobHandle m_BoundJobHandle; JobHandle m_DeformJobHandle; JobHandle m_CopyJobHandle; [SerializeField] GameObject m_Helper; internal GameObject helperGameObject => m_Helper; internal void RemoveTransformById(int transformId) { m_LocalToWorldTransformAccessJob.RemoveTransformById(transformId); } internal void AddSpriteSkinBoneTransform(SpriteSkin spriteSkin) { if (spriteSkin == null) return; if (spriteSkin.boneTransforms != null) { foreach (var t in spriteSkin.boneTransforms) { if(t != null) m_LocalToWorldTransformAccessJob.AddTransform(t); } } } internal void AddSpriteSkinRootBoneTransform(SpriteSkin spriteSkin) { if (spriteSkin == null || spriteSkin.rootBone == null) return; m_LocalToWorldTransformAccessJob.AddTransform(spriteSkin.rootBone); } internal void AddSpriteSkin(SpriteSkin spriteSkin) { if (spriteSkin == null) return; var added = m_SpriteSkins.Contains(spriteSkin); Debug.Assert(!added, $"SpriteSkin {spriteSkin.gameObject.name} is already added"); if (!added) { m_SpriteSkins.Add(spriteSkin); var count = m_SpriteSkins.Count; Array.Resize(ref m_SpriteRenderers, count); m_SpriteRenderers[count - 1] = spriteSkin.spriteRenderer; m_WorldToLocalTransformAccessJob.AddTransform(spriteSkin.transform); if (m_IsSpriteSkinActiveForDeform.IsCreated) { NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_IsSpriteSkinActiveForDeform, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_PerSkinJobData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_SpriteSkinData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BoundsData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_Buffers, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BufferSizes, count); CopyToSpriteSkinData(count - 1); } } } internal void CopyToSpriteSkinData(SpriteSkin spriteSkin) { if (spriteSkin == null) return; var index = m_SpriteSkins.IndexOf(spriteSkin); if (index < 0) return; CopyToSpriteSkinData(index); } private void CopyToSpriteSkinData(int index) { if (index < 0 || index >= m_SpriteSkins.Count || !m_SpriteSkinData.IsCreated) return; var spriteSkinData = default(SpriteSkinData); var spriteSkin = m_SpriteSkins[index]; spriteSkin.CopyToSpriteSkinData(ref spriteSkinData, index); m_SpriteSkinData[index] = spriteSkinData; m_SpriteRenderers[index] = spriteSkin.spriteRenderer; } internal void RemoveSpriteSkin(SpriteSkin spriteSkin) { var index = m_SpriteSkins.IndexOf(spriteSkin); if (index < 0) return; // Check if it is not the last SpriteSkin if (index < m_SpriteSkins.Count - 1) { m_SpriteSkins.RemoveAtSwapBack(index); CopyToSpriteSkinData(index); } else { m_SpriteSkins.RemoveAt(index); } var count = m_SpriteSkins.Count; Array.Resize(ref m_SpriteRenderers, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_IsSpriteSkinActiveForDeform, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_PerSkinJobData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_SpriteSkinData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BoundsData, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_Buffers, count); NativeArrayHelpers.ResizeAndCopyIfNeeded(ref m_BufferSizes, count); m_WorldToLocalTransformAccessJob.RemoveTransform(spriteSkin.transform); } internal void AddSpriteSkinForLateUpdate(SpriteSkin spriteSkin) { if (spriteSkin == null) return; var added = m_SpriteSkinLateUpdate.Contains(spriteSkin); Debug.Assert(!added, $"SpriteSkin {spriteSkin.gameObject.name} is already added for LateUpdate"); if (!added) { m_SpriteSkinLateUpdate.Add(spriteSkin); } } internal void RemoveSpriteSkinForLateUpdate(SpriteSkin spriteSkin) { m_SpriteSkinLateUpdate.Remove(spriteSkin); } void Init() { if (m_LocalToWorldTransformAccessJob == null) m_LocalToWorldTransformAccessJob = new TransformAccessJob(); if (m_WorldToLocalTransformAccessJob == null) m_WorldToLocalTransformAccessJob = new TransformAccessJob(); CreateHelper(); } void CreateHelper() { if (m_Helper != null) return; m_Helper = new GameObject("SpriteSkinUpdateHelper"); m_Helper.hideFlags = HideFlags.HideAndDontSave; var helperComponent = m_Helper.AddComponent(); helperComponent.onDestroyingComponent += OnHelperDestroyed; #if !UNITY_EDITOR GameObject.DontDestroyOnLoad(m_Helper); #endif } void OnHelperDestroyed(GameObject helperGo) { if (m_Helper != helperGo) return; m_Helper = null; CreateHelper(); } internal void ResetComposite() { foreach (var spriteSkin in m_SpriteSkins) spriteSkin.batchSkinning = false; m_SpriteSkins.Clear(); m_LocalToWorldTransformAccessJob.Destroy(); m_WorldToLocalTransformAccessJob.Destroy(); m_LocalToWorldTransformAccessJob = new TransformAccessJob(); m_WorldToLocalTransformAccessJob = new TransformAccessJob(); } public void OnEnable() { s_Instance = this; m_FinalBoneTransforms = new NativeArray(1, Allocator.Persistent); m_BoneLookupData = new NativeArray(1, Allocator.Persistent); m_VertexLookupData = new NativeArray(1, Allocator.Persistent); m_SkinBatchArray = new NativeArray(1, Allocator.Persistent); Init(); // Initialise all existing SpriteSkins as execution order is indeterminate var count = m_SpriteSkins.Count; if (count > 0) { m_IsSpriteSkinActiveForDeform = new NativeArray(count, Allocator.Persistent); m_PerSkinJobData = new NativeArray(count, Allocator.Persistent); m_SpriteSkinData = new NativeArray(count, Allocator.Persistent); m_BoundsData = new NativeArray(count, Allocator.Persistent); m_Buffers = new NativeArray(count, Allocator.Persistent); m_BufferSizes = new NativeArray(count, Allocator.Persistent); for (var i = 0; i < count; ++i) { var spriteSkin = m_SpriteSkins[i]; spriteSkin.batchSkinning = true; CopyToSpriteSkinData(i); } } else { m_IsSpriteSkinActiveForDeform = new NativeArray(1, Allocator.Persistent); m_PerSkinJobData = new NativeArray(1, Allocator.Persistent); m_SpriteSkinData = new NativeArray(1, Allocator.Persistent); m_BoundsData = new NativeArray(1, Allocator.Persistent); m_Buffers = new NativeArray(1, Allocator.Persistent); m_BufferSizes = new NativeArray(1, Allocator.Persistent); } } void OnDisable() { m_DeformJobHandle.Complete(); m_BoundJobHandle.Complete(); m_CopyJobHandle.Complete(); foreach (var spriteSkin in m_SpriteSkins) spriteSkin.batchSkinning = false; m_SpriteSkins.Clear(); m_SpriteRenderers = new SpriteRenderer[0]; BufferManager.instance.ReturnBuffer(GetInstanceID()); m_IsSpriteSkinActiveForDeform.DisposeIfCreated(); m_PerSkinJobData.DisposeIfCreated(); m_SpriteSkinData.DisposeIfCreated(); m_Buffers.DisposeIfCreated(); m_BufferSizes.DisposeIfCreated(); m_BoneLookupData.DisposeIfCreated(); m_VertexLookupData.DisposeIfCreated(); m_SkinBatchArray.DisposeIfCreated(); m_FinalBoneTransforms.DisposeIfCreated(); m_BoundsData.DisposeIfCreated(); if (m_Helper != null) { m_Helper.GetComponent().onDestroyingComponent -= OnHelperDestroyed; GameObject.DestroyImmediate(m_Helper); } m_LocalToWorldTransformAccessJob.Destroy(); m_WorldToLocalTransformAccessJob.Destroy(); } internal unsafe void LateUpdate() { foreach (var ss in m_SpriteSkinLateUpdate) { if(ss != null) ss.OnLateUpdate(); } var count = m_SpriteSkins.Count; if (count == 0) return; Profiling.prepareData.Begin(); Assert.AreEqual(m_IsSpriteSkinActiveForDeform.Length, count); Assert.AreEqual(m_PerSkinJobData.Length, count); Assert.AreEqual(m_SpriteSkinData.Length, count); Assert.AreEqual(m_BoundsData.Length, count); Assert.AreEqual(m_Buffers.Length, count); Assert.AreEqual(m_BufferSizes.Length, count); Assert.AreEqual(m_SpriteRenderers.Length, count); using (Profiling.validateSpriteSkinData.Auto()) { for (var i = 0; i < m_SpriteSkins.Count; ++i) { var spriteSkin = m_SpriteSkins[i]; m_IsSpriteSkinActiveForDeform[i] = spriteSkin.BatchValidate(); if (m_IsSpriteSkinActiveForDeform[i] && spriteSkin.NeedUpdateCompositeCache()) { CopyToSpriteSkinData(i); } } } Profiling.transformAccessJob.Begin(); var localToWorldJobHandle = m_LocalToWorldTransformAccessJob.StartLocalToWorldJob(); var worldToLocalJobHandle = m_WorldToLocalTransformAccessJob.StartWorldToLocalJob(); Profiling.transformAccessJob.End(); using (Profiling.getSpriteSkinBatchData.Auto()) { NativeArrayHelpers.ResizeIfNeeded(ref m_SkinBatchArray, 1); var fillPerSkinJobSingleThread = new FillPerSkinJobSingleThread() { isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform, combinedSkinBatchArray = m_SkinBatchArray, spriteSkinDataArray = m_SpriteSkinData, perSkinJobDataArray = m_PerSkinJobData, }; fillPerSkinJobSingleThread.Run(); } Profiling.prepareData.End(); var skinBatch = m_SkinBatchArray[0]; var batchCount = m_SpriteSkinData.Length; var vertexBufferSize = skinBatch.deformVerticesStartPos; if (vertexBufferSize <= 0) { localToWorldJobHandle.Complete(); worldToLocalJobHandle.Complete(); DeactivateDeformableBuffers(); return; } using (Profiling.resizeBuffers.Auto()) { m_DeformedVerticesBuffer = BufferManager.instance.GetBuffer(GetInstanceID(), vertexBufferSize); NativeArrayHelpers.ResizeIfNeeded(ref m_FinalBoneTransforms, skinBatch.bindPosesIndex.y); NativeArrayHelpers.ResizeIfNeeded(ref m_BoneLookupData, skinBatch.bindPosesIndex.y); NativeArrayHelpers.ResizeIfNeeded(ref m_VertexLookupData, skinBatch.verticesIndex.y); } Profiling.prepare.Begin(); var prepareJob = new PrepareDeformJob { batchDataSize = batchCount, perSkinJobData = m_PerSkinJobData, boneLookupData = m_BoneLookupData, vertexLookupData = m_VertexLookupData }; var jobHandle = prepareJob.Schedule(); Profiling.prepare.End(); Profiling.scheduleJobs.Begin(); var boneJobBatched = new BoneDeformBatchedJob() { boneTransform = m_LocalToWorldTransformAccessJob.transformMatrix, rootTransform = m_WorldToLocalTransformAccessJob.transformMatrix, spriteSkinData = m_SpriteSkinData, boneLookupData = m_BoneLookupData, finalBoneTransforms = m_FinalBoneTransforms, rootTransformIndex = m_WorldToLocalTransformAccessJob.transformData, boneTransformIndex = m_LocalToWorldTransformAccessJob.transformData }; jobHandle = JobHandle.CombineDependencies(localToWorldJobHandle, worldToLocalJobHandle, jobHandle); jobHandle = boneJobBatched.Schedule(skinBatch.bindPosesIndex.y, 8, jobHandle); var skinJobBatched = new SkinDeformBatchedJob() { vertices = m_DeformedVerticesBuffer.array, vertexLookupData = m_VertexLookupData, spriteSkinData = m_SpriteSkinData, perSkinJobData = m_PerSkinJobData, finalBoneTransforms = m_FinalBoneTransforms, }; m_DeformJobHandle = skinJobBatched.Schedule(skinBatch.verticesIndex.y, 16, jobHandle); var copySpriteRendererBuffersJob = new CopySpriteRendererBuffersJob() { isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform, spriteSkinData = m_SpriteSkinData, ptrVertices = (IntPtr) NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_DeformedVerticesBuffer.array), buffers = m_Buffers, bufferSizes = m_BufferSizes, }; m_CopyJobHandle = copySpriteRendererBuffersJob.Schedule(batchCount, 16, jobHandle); var updateBoundJob = new CalculateSpriteSkinAABBJob { vertices = m_DeformedVerticesBuffer.array, isSpriteSkinValidForDeformArray = m_IsSpriteSkinActiveForDeform, spriteSkinData = m_SpriteSkinData, bounds = m_BoundsData, }; m_BoundJobHandle = updateBoundJob.Schedule(batchCount, 4, m_DeformJobHandle); Profiling.scheduleJobs.End(); JobHandle.ScheduleBatchedJobs(); jobHandle = JobHandle.CombineDependencies(m_BoundJobHandle, m_CopyJobHandle); jobHandle.Complete(); using (Profiling.setBatchDeformableBufferAndLocalAABB.Auto()) { InternalEngineBridge.SetBatchDeformableBufferAndLocalAABBArray(m_SpriteRenderers, m_Buffers, m_BufferSizes, m_BoundsData); } DeactivateDeformableBuffers(); } void DeactivateDeformableBuffers() { using (Profiling.deactivateDeformableBuffer.Auto()) { for (var i = 0; i < m_IsSpriteSkinActiveForDeform.Length; ++i) { if (m_IsSpriteSkinActiveForDeform[i] || InternalEngineBridge.IsUsingDeformableBuffer(m_SpriteRenderers[i], IntPtr.Zero)) continue; m_SpriteRenderers[i].DeactivateDeformableBuffer(); } } } internal bool HasDeformableBufferForSprite(int dataIndex) { if (dataIndex < 0 && m_IsSpriteSkinActiveForDeform.Length >= dataIndex) throw new InvalidOperationException("Invalid index for deformable buffer"); return m_IsSpriteSkinActiveForDeform[dataIndex]; } internal unsafe NativeArray GetDeformableBufferForSprite(int dataIndex) { if (dataIndex < 0 && m_SpriteSkinData.Length >= dataIndex) throw new InvalidOperationException("Invalid index for deformable buffer"); if (!m_DeformJobHandle.IsCompleted) m_DeformJobHandle.Complete(); var skinData = m_SpriteSkinData[dataIndex]; if (skinData.deformVerticesStartPos < 0) throw new InvalidOperationException("There are no currently deformed vertices."); var vertexBufferLength = skinData.spriteVertexCount * skinData.spriteVertexStreamSize; byte* ptrVertices = (byte*)m_DeformedVerticesBuffer.array.GetUnsafeReadOnlyPtr(); ptrVertices += skinData.deformVerticesStartPos; var buffer = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptrVertices, vertexBufferLength, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref buffer, NativeArrayUnsafeUtility.GetAtomicSafetyHandle(m_DeformedVerticesBuffer.array)); #endif return buffer; } // Code for tests #region Code For Tests internal string GetDebugLog() { var log = ""; log = "====SpriteSkinLateUpdate===\n"; log += "Count: " + m_SpriteSkinLateUpdate.Count +"\n"; foreach (var ss in m_SpriteSkinLateUpdate) { log += ss == null ? "null" : ss.name; log += "\n"; } log += "\n"; log += "===SpriteSkinBatch===\n"; log += "Count: " + m_SpriteSkins.Count +"\n"; foreach (var ss in m_SpriteSkins) { log += ss == null ? "null" : ss.name; log += "\n"; } log += "===LocalToWorldTransformAccessJob===\n"; log += m_LocalToWorldTransformAccessJob.GetDebugLog(); log += "\n"; log += "===WorldToLocalTransformAccessJob===\n"; log += "\n"; log += m_WorldToLocalTransformAccessJob.GetDebugLog(); return log; } internal SpriteSkin[] GetSpriteSkins() { return m_SpriteSkins.ToArray(); } internal TransformAccessJob GetWorldToLocalTransformAccessJob() { return m_WorldToLocalTransformAccessJob; } internal TransformAccessJob GetLocalToWorldTransformAccessJob() { return m_LocalToWorldTransformAccessJob; } #endregion } #if UNITY_EDITOR [UnityEditor.InitializeOnLoad] internal class SpriteSkinCompositeStartup { static SpriteSkinCompositeStartup() { if (null == SpriteSkinComposite.instance.helperGameObject) throw new System.InvalidOperationException("SpriteSkinComposite not initialized properly."); } } #endif } #endif