using System; using System.Collections.Generic; using Unity.Assertions; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; using Unity.Rendering; using UnityEngine; using UnityEngine.Rendering; using Rukhanka.Toolbox; using Unity.Burst; using Hash128 = Unity.Entities.Hash128; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace Rukhanka { public partial struct GPUAnimationPreparationSystem { [BurstCompile] unsafe struct ResetFrameDataJob: IJob { public NativeParallelHashMap> newAnimationsMap; public NativeParallelHashMap> newRigsMap; public NativeParallelHashMap> newAvatarMasksMap; public NativeList newRigsList; public NativeList newAnimationsList; public NativeList newAvatarMasksList; public NativeParallelHashMap newSkinnedMeshDataMap; public NativeList newSkinnedMeshDataList; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPUAnimationClipsDataSize; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPURigsCount; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPURigBonesCount; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPUBoneRemapIndicesCount; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPUBoneRemapTablesCount; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPUHumanRotationDataEntriesCount; [NativeDisableUnsafePtrRestriction] public uint2 *totalGPUAvatarMasksDataCount; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { newAnimationsMap.Clear(); newRigsMap.Clear(); newAvatarMasksMap.Clear(); newRigsList.Clear(); newAnimationsList.Clear(); newAvatarMasksList.Clear(); newSkinnedMeshDataMap.Clear(); foreach (var nbrt in newSkinnedMeshDataList) { nbrt.boneRemapTableBlob.Dispose(); } newSkinnedMeshDataList.Clear(); totalGPUAnimationClipsDataSize->y = totalGPUAnimationClipsDataSize->x; totalGPURigsCount->y = totalGPURigsCount->x; totalGPURigBonesCount->y = totalGPURigBonesCount->x; totalGPUBoneRemapIndicesCount->y = totalGPUBoneRemapIndicesCount->x; totalGPUBoneRemapTablesCount->y = totalGPUBoneRemapTablesCount->x; totalGPUHumanRotationDataEntriesCount->y = totalGPUHumanRotationDataEntriesCount->x; totalGPUAvatarMasksDataCount->y = totalGPUAvatarMasksDataCount->x; } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] [WithAll(typeof(GPUAnimationEngineTag))] partial struct GatherNewRigsAndAnimationsJob: IJobEntity { [ReadOnly] public NativeParallelHashMap existingAnimations; [ReadOnly] public NativeParallelHashMap existingRigs; [ReadOnly] public NativeParallelHashMap existingAvatarMasks; [NativeDisableContainerSafetyRestriction] public NativeParallelHashMap>.ParallelWriter newFrameAnimations; [NativeDisableContainerSafetyRestriction] public NativeParallelHashMap>.ParallelWriter newRigDefinitions; [NativeDisableContainerSafetyRestriction] public NativeParallelHashMap>.ParallelWriter newFrameAvatarMasks; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Execute(in RigDefinitionComponent rdc, in DynamicBuffer atps) { var rigHash = rdc.rigBlob.Value.hash; if (!existingRigs.TryGetValue(rigHash, out _)) { newRigDefinitions.TryAdd(rigHash, rdc.rigBlob); } for (var i = 0; i < atps.Length; ++i) { var atp = atps[i]; if (!atp.animation.IsCreated) continue; // Add animation var animationHash = atp.animation.Value.hash; if (!existingAnimations.TryGetValue(animationHash, out _)) { newFrameAnimations.TryAdd(animationHash, atp.animation); } // Add avatar mask if (atp.avatarMask.IsCreated) { var avatarMaskHash = atp.avatarMask.Value.hash; if (!existingAvatarMasks.TryGetValue(avatarMaskHash, out _)) { newFrameAvatarMasks.TryAdd(avatarMaskHash, atp.avatarMask); } } } } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CreateNewAnimationsListJob: IJob { [ReadOnly] public NativeParallelHashMap> newBlobAssetsMap; public NativeList outList; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity); foreach (var nba in newBlobAssetsMap) { var acp = new GPUAnimationClipPlacementData() { animationClipBlob = nba.Value }; outList.Add(acp); } } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CreateNewAvatarMasksListJob: IJob { [ReadOnly] public NativeParallelHashMap> newBlobAssetsMap; public NativeList outList; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity); foreach (var nba in newBlobAssetsMap) { var acp = new GPUAvatarMaskPlacementData() { avatarMaskBlob = nba.Value }; outList.Add(acp); } } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CreateNewRigsListJob: IJob { [ReadOnly] public NativeParallelHashMap> newBlobAssetsMap; public NativeList outList; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity); foreach (var nba in newBlobAssetsMap) { var rdp = new GPURigDefinitionPlacementData() { rigBlob = nba.Value, }; outList.Add(rdp); } } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CreateNewSkinnedMeshesDataListJob: IJob { [ReadOnly] public NativeParallelHashMap newSkinnedMeshesDataMap; public NativeList outList; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { outList.Capacity = math.max(newSkinnedMeshesDataMap.Count(), outList.Capacity); foreach (var nba in newSkinnedMeshesDataMap) { outList.Add(nba.Value); } } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] partial struct NewFrameRigToSkinnedMeshRemapTablesJob: IJobEntity { [ReadOnly] public NativeParallelHashMap skinnedMeshesDataMap; [ReadOnly] public ComponentLookup gpuAnimationEngineComponentLookup; [ReadOnly] public ComponentLookup rigDefComponentLookup; [WriteOnly, NativeDisableContainerSafetyRestriction] public NativeParallelHashMap.ParallelWriter newFrameSkinnedMeshData; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Execute(in SkinnedMeshRendererComponent asmc) { if (!asmc.IsGPUAnimator(gpuAnimationEngineComponentLookup)) return; rigDefComponentLookup.TryGetComponent(asmc.animatedRigEntity, out var rigDef); var hash = AnimationUtils.CalculateBoneRemapTableHash(asmc.smrInfoBlob, rigDef.rigBlob); if (skinnedMeshesDataMap.TryGetValue(hash, out _)) return; var remapTableBlob = AnimationUtils.MakeSkinnedMeshToRigRemapTable(asmc, rigDef, Allocator.TempJob); var skinnedMeshInfo = new GPUSkinnedMeshPlacementData() { hash = hash, dataOffset = -1, skinnedMeshInfo = asmc.smrInfoBlob, boneRemapTableBlob = remapTableBlob }; if (!newFrameSkinnedMeshData.TryAdd(hash, skinnedMeshInfo)) remapTableBlob.Dispose(); } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CalculateNewAnimationOffsets: IJobParallelForDefer { [NativeDisableParallelForRestriction] public NativeList newAnimationClips; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalAnimationClipsOffsetCounter; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute(int index) { ref var ac = ref newAnimationClips.ElementAt(index); ref var acb = ref ac.animationClipBlob.Value; var clipSize = CalculateGPUAnimationClipPlacementData(ref acb, ref ac); ac.animationClipDataOffset = totalAnimationClipsOffsetCounter.Add(clipSize); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int CalculateGPUAnimationClipPlacementData(ref AnimationClipBlob acb, ref GPUAnimationClipPlacementData acpd) { var totalBytes = 0; totalBytes += UnsafeUtility.SizeOf(); totalBytes = CalculateGPUTrackSetSize(totalBytes, ref acb.clipTracks, ref acpd.clipTracks); totalBytes = CalculateGPUTrackSetSize(totalBytes, ref acb.additiveReferencePoseFrame, ref acpd.additiveRefPoseTracks); return totalBytes; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int CalculateGPUTrackSetSize(int currentOffset, ref TrackSet ts, ref GPUTrackSetPlacementData tpd) { var rv = currentOffset; if (ts.keyframes.Length == 0) { tpd = GPUTrackSetPlacementData.Empty(); return rv; } tpd.keyFramesOffset = rv; rv += ts.keyframes.Length * UnsafeUtility.SizeOf(); tpd.tracksOffset = rv; rv += ts.tracks.Length * UnsafeUtility.SizeOf(); tpd.trackGroupsOffset = rv; rv += ts.trackGroups.Length * UnsafeUtility.SizeOf(); tpd.hashTableOffset = rv; rv += ts.trackGroupPHT.pht.Length * UnsafeUtility.SizeOf(); return rv; } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CalculateNewAvatarMasksOffsetsJob: IJobParallelForDefer { [NativeDisableParallelForRestriction] public NativeList newAvatarMasks; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalAvatarMasksCounter; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute(int index) { ref var am = ref newAvatarMasks.ElementAt(index); ref var amb = ref am.avatarMaskBlob.Value; // Plus one because we need space for human body parts mask am.dataOffset = totalAvatarMasksCounter.Add(amb.includedBoneMask.Length + 1); } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CalculateNewRigsOffsets: IJobParallelForDefer { [NativeDisableParallelForRestriction] public NativeList newRigs; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalGPURigsCount; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalGPURigBonesCount; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalGPUHumanRotationDataEntriesCount; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute(int index) { ref var rig = ref newRigs.ElementAt(index); rig.rigDefinitionIndex = totalGPURigsCount.Add(1); ref var rb = ref rig.rigBlob.Value; rig.rigBonesOffset = totalGPURigBonesCount.Add(rb.bones.Length); rig.humanRotationDataOffset = -1; if (rb.humanData.IsValid) rig.humanRotationDataOffset = totalGPUHumanRotationDataEntriesCount.Add(rb.humanData.Value.humanRotData.Length); } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] struct CalculateNewBoneRemapTablesOffsetsJob: IJobParallelForDefer { [NativeDisableParallelForRestriction] public NativeList newBoneRemapTables; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalGPUBoneRemapTablesCount; [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 totalGPUBoneRemapIndicesCount; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute(int index) { ref var rig = ref newBoneRemapTables.ElementAt(index); rig.dataOffset = totalGPUBoneRemapIndicesCount.Add(rig.boneRemapTableBlob.Value.remapIndices.Length); totalGPUBoneRemapTablesCount.Add(1); } } //-----------------------------------------------------------------------------------------------------------------// [BurstCompile] unsafe struct RegisterNewResourcesJob: IJob { [ReadOnly] public NativeList newAnimations; [ReadOnly] public NativeList newRigDefs; [ReadOnly] public NativeList newSkinnedMeshesDatas; [ReadOnly] public NativeList newAvatarMasks; public NativeParallelHashMap animationClipsOffsets; public NativeParallelHashMap rigDefinitionOffsets; public NativeParallelHashMap skinnedMeshDataOffsets; public NativeParallelHashMap avatarMasksDataOffsets; [NativeDisableUnsafePtrRestriction] public uint *maximumKeyFrameArrayLength; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Execute() { foreach (var a in newAnimations) { animationClipsOffsets.Add(a.animationClipBlob.Value.hash, a.animationClipDataOffset); *maximumKeyFrameArrayLength = math.max(*maximumKeyFrameArrayLength, a.animationClipBlob.Value.maxTrackKeyframeLength); } foreach (var a in newRigDefs) { rigDefinitionOffsets.Add(a.rigBlob.Value.hash, a.rigDefinitionIndex); } foreach (var a in newSkinnedMeshesDatas) { skinnedMeshDataOffsets.Add(a.hash, a.dataOffset); } foreach (var a in newAvatarMasks) { avatarMasksDataOffsets.Add(a.avatarMaskBlob.Value.hash, a.dataOffset); } } } } }