Netcode Bootstrap
This commit is contained in:
+580
@@ -0,0 +1,580 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Assertions;
|
||||
using Unity.Rendering;
|
||||
using Unity.Transforms;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
|
||||
[UpdateAfter(typeof(SkinnedMeshPreparationSystem))]
|
||||
[UpdateBefore(typeof(MeshDeformationSystem))]
|
||||
public partial class GPUAnimationSystem: SystemBase
|
||||
{
|
||||
ComputeShader animationEngineCS;
|
||||
// Buffers for persistent GPU data. Equivalent for blob asset store
|
||||
GraphicsBuffer animationClipsGB;
|
||||
GraphicsBuffer avatarMasksGB;
|
||||
GraphicsBuffer rigDefinitionGB;
|
||||
GraphicsBuffer rigHumanAvatarRotationDataGB;
|
||||
GraphicsBuffer rigBonesGB;
|
||||
GraphicsBuffer skinnedMeshBoneDataGB;
|
||||
|
||||
SparseUploaderPool sparseUploaderPool;
|
||||
|
||||
// Buffers for per frame animation data (workload, animation to process). Equivalent for animation components
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimationToProcess> frameAnimationToProcessGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimatedBoneWorkload> framePerBoneAnimationWorkloadGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimationJob> frameRigAnimationJobsGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.SkinnedMeshWorkload> frameSkinMatrixWorkloadGB;
|
||||
|
||||
// Buffers filled by compute shaders
|
||||
GraphicsBuffer animatedBonesGB;
|
||||
GraphicsBuffer rigSpaceAnimatedBonesGB;
|
||||
|
||||
ComputeKernel processAnimationKernel;
|
||||
ComputeKernel makeRigSpaceBoneTransformsKernel;
|
||||
ComputeKernel makeSkinMatricesKernel;
|
||||
ComputeKernel copyBufferKernel;
|
||||
ComputeKernel copyMeshDataKernel;
|
||||
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> skinnedMeshToRigBoneRemapTables;
|
||||
EntityQuery gpuAnimatedRigQuery, gpuAnimatedRigNoChunkComponentsQuery, smrQuery;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
BoneVisualizationSystem boneVisualizationSystem;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnCreate()
|
||||
{
|
||||
if (!SystemInfo.supportsComputeShaders)
|
||||
{
|
||||
Debug.LogWarning("Compute shaders is not supported on current platform. GPU animation engine is disabled.");
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !HYBRID_RENDERER_DISABLED
|
||||
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
|
||||
#endif
|
||||
{
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
skinnedMeshToRigBoneRemapTables = new (0xff, Allocator.Persistent);
|
||||
|
||||
CreateComputeBuffers();
|
||||
|
||||
animationEngineCS = Resources.Load<ComputeShader>("GPUAnimationEngine");
|
||||
processAnimationKernel = new ComputeKernel(animationEngineCS, "ProcessAnimations");
|
||||
makeRigSpaceBoneTransformsKernel = new ComputeKernel(animationEngineCS, "MakeRigSpaceBoneTransforms");
|
||||
makeSkinMatricesKernel = new ComputeKernel(animationEngineCS, "ComputeSkinMatrices");
|
||||
|
||||
gpuAnimatedRigQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<RigDefinitionComponent, AnimationToProcessComponent, GPUAnimationEngineTag>()
|
||||
.WithAllChunkComponent<GPURigFrameOffsetsComponent>()
|
||||
.WithNone<CullAnimationsTag>()
|
||||
.Build(EntityManager);
|
||||
|
||||
gpuAnimatedRigNoChunkComponentsQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<RigDefinitionComponent, AnimationToProcessComponent, GPUAnimationEngineTag>()
|
||||
.WithNoneChunkComponent<GPURigFrameOffsetsComponent>()
|
||||
.WithNone<CullAnimationsTag>()
|
||||
.Build(EntityManager);
|
||||
|
||||
smrQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<SkinnedMeshRendererComponent, LocalToWorld>()
|
||||
.Build(EntityManager);
|
||||
|
||||
Assert.IsTrue(UnsafeUtility.SizeOf<GPUStructures.KeyFrame>() == UnsafeUtility.SizeOf<KeyFrame>());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
ref var runtimeAnimationData = ref SystemAPI.GetSingletonRW<GPURuntimeAnimationData>().ValueRW;
|
||||
|
||||
EntityManager.AddChunkComponentData<GPURigFrameOffsetsComponent>(gpuAnimatedRigNoChunkComponentsQuery, default);
|
||||
|
||||
PrepareFrameAnimationData(ref runtimeAnimationData, Dependency);
|
||||
DispatchAnimationCalculation(ref runtimeAnimationData);
|
||||
SetBuffersForBoneRenderer(ref runtimeAnimationData);
|
||||
GPUBuffersEndFrame();
|
||||
sparseUploaderPool.FrameCleanup();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
animationClipsGB?.Release();
|
||||
avatarMasksGB?.Release();
|
||||
rigDefinitionGB?.Release();
|
||||
rigHumanAvatarRotationDataGB?.Release();
|
||||
rigBonesGB?.Release();
|
||||
skinnedMeshBoneDataGB?.Release();
|
||||
|
||||
sparseUploaderPool?.Dispose();
|
||||
|
||||
frameAnimationToProcessGB?.Dispose();
|
||||
framePerBoneAnimationWorkloadGB?.Dispose();
|
||||
frameRigAnimationJobsGB?.Dispose();
|
||||
frameSkinMatrixWorkloadGB?.Dispose();
|
||||
|
||||
animatedBonesGB?.Dispose();
|
||||
rigSpaceAnimatedBonesGB?.Dispose();
|
||||
|
||||
if (skinnedMeshToRigBoneRemapTables.IsCreated)
|
||||
{
|
||||
foreach (var remapTable in skinnedMeshToRigBoneRemapTables)
|
||||
{
|
||||
remapTable.Value.Dispose();
|
||||
}
|
||||
}
|
||||
skinnedMeshToRigBoneRemapTables.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateComputeBuffers()
|
||||
{
|
||||
var bufferInitialCapacity = 0x4;
|
||||
frameAnimationToProcessGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
framePerBoneAnimationWorkloadGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
frameRigAnimationJobsGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
frameSkinMatrixWorkloadGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
|
||||
animatedBonesGB = new (GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, bufferInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.BoneTransform>());
|
||||
rigSpaceAnimatedBonesGB = new (GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, bufferInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.BoneTransform>());
|
||||
|
||||
// Persistent data buffers
|
||||
var persistentBuffersInitialCapacity = 0x4;
|
||||
animationClipsGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<uint>());
|
||||
avatarMasksGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<uint>());
|
||||
rigDefinitionGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.RigDefinition>());
|
||||
rigHumanAvatarRotationDataGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>());
|
||||
rigBonesGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.RigBone>());
|
||||
skinnedMeshBoneDataGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.SkinnedMeshBoneData>());
|
||||
|
||||
sparseUploaderPool = new SparseUploaderPool();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle CalculateFrameWorksetData(ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
// All following jobs should be sequential. Interlocked math for a few counters are significantly slower than
|
||||
// sequential execution
|
||||
|
||||
var gpuRigFrameOffsetsTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>();
|
||||
var rigDefTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true);
|
||||
var atpBufHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>(true);
|
||||
|
||||
// Workload sizes for animation calculation per chunk
|
||||
var computeRigsWorkloadSizesJob = new ComputeFrameRigWorkloadSizesPerChunkJob()
|
||||
{
|
||||
frameOffsetsTypeHandle = gpuRigFrameOffsetsTypeHandle,
|
||||
atpBufTypeHandle = atpBufHandle,
|
||||
rigDefComponentTypeHandle = rigDefTypeHandle,
|
||||
};
|
||||
var computeRigsWorkloadSizesJH = computeRigsWorkloadSizesJob.ScheduleParallel(gpuAnimatedRigQuery, dependsOn);
|
||||
|
||||
// Make absolute chunk offsets
|
||||
var computeAbsChunkOffsetsJob = new ComputeFrameRigWorkloadSizesAbsChunkOffsetsJob()
|
||||
{
|
||||
frameAnimatedBonesCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimatedBonesCounter),
|
||||
frameAnimatedRigsCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimatedRigsCounter),
|
||||
frameAnimationToProcessCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimationToProcessCounter),
|
||||
gpuRigChunkDataTypeHandle = gpuRigFrameOffsetsTypeHandle
|
||||
};
|
||||
var computeAbsChunkOffsetsJH = computeAbsChunkOffsetsJob.Schedule(gpuAnimatedRigQuery, computeRigsWorkloadSizesJH);
|
||||
|
||||
// Workload sizes for skin matrices calculation
|
||||
// To avoid InterlockedAdd (it is slow when invocation count is really big), and single thread job I use 2-step
|
||||
// process here: first job increments per-thread counters, and another single thread job do final accumulate.
|
||||
var computeSkinnedMeshCountJob = new ComputeFrameSkinnedMeshWorkloadSizesJob()
|
||||
{
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters,
|
||||
gpuAnimationEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true)
|
||||
};
|
||||
var computeSkinnedMeshCountTotalJob = new ComputeFrameSkinnedMeshWorkloadSizesTotalJob()
|
||||
{
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters,
|
||||
frameSkinnedMeshesCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter)
|
||||
};
|
||||
|
||||
var computeSkinnedMeshCountJH = computeSkinnedMeshCountJob.ScheduleParallel(dependsOn);
|
||||
var computeSkinnedMeshCountTotalJH = computeSkinnedMeshCountTotalJob.Schedule(computeSkinnedMeshCountJH);
|
||||
|
||||
var rv = JobHandle.CombineDependencies(computeAbsChunkOffsetsJH, computeSkinnedMeshCountTotalJH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GPUBuffersBeginFrame()
|
||||
{
|
||||
frameAnimationToProcessGB.BeginFrame();
|
||||
framePerBoneAnimationWorkloadGB.BeginFrame();
|
||||
frameRigAnimationJobsGB.BeginFrame();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GPUBuffersEndFrame()
|
||||
{
|
||||
frameAnimationToProcessGB.EndFrame();
|
||||
framePerBoneAnimationWorkloadGB.EndFrame();
|
||||
frameRigAnimationJobsGB.EndFrame();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void FillFrameSkinMatrixCalculationWorkloadGPUBuffers
|
||||
(
|
||||
ref GPURuntimeAnimationData rad,
|
||||
in NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap
|
||||
)
|
||||
{
|
||||
var frameSkinMatrixWorkloadBuf = frameSkinMatrixWorkloadGB.LockBufferForWrite(0, (int)rad.frameSkinnedMeshesCounter);
|
||||
rad.frameSkinnedMeshesCounter = 0;
|
||||
var frameSkinnedMeshesAtomicCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter));
|
||||
|
||||
var fillFrameSkinMatrixWorkloadBuffersJob = new FillFrameSkinMatrixWorkloadBuffersJob()
|
||||
{
|
||||
frameSkinnedMeshesAtomicCounter = frameSkinnedMeshesAtomicCounter,
|
||||
frameSkinMatrixWorkloadBuf = frameSkinMatrixWorkloadBuf,
|
||||
skinnedMeshDataMap = rad.skinnedMeshesDataMap,
|
||||
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true),
|
||||
gpuAnimationEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true),
|
||||
localToWorldLookup = SystemAPI.GetComponentLookup<LocalToWorld>(true),
|
||||
entityToSMRFrameDataMap = entityToSMRFrameDataMap,
|
||||
gpuRigChunkDataTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>(true),
|
||||
smrTypeHandle = SystemAPI.GetComponentTypeHandle<SkinnedMeshRendererComponent>(true),
|
||||
l2wTypeHandle = SystemAPI.GetComponentTypeHandle<LocalToWorld>(true),
|
||||
entityTypeHandle = SystemAPI.GetEntityTypeHandle(),
|
||||
frameOffsetsLookup = SystemAPI.GetComponentLookup<GPURigFrameOffsetsComponent>(true)
|
||||
};
|
||||
fillFrameSkinMatrixWorkloadBuffersJob.ScheduleParallel(smrQuery, default).Complete();
|
||||
|
||||
frameSkinMatrixWorkloadGB.UnlockBufferAfterWrite((int)rad.frameSkinnedMeshesCounter);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FillFrameAnimationCalculationWorkloadGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
var animatedBonesWorkloadBuf = framePerBoneAnimationWorkloadGB.LockBufferForWrite(0, (int)rad.frameAnimatedBonesCounter);
|
||||
var animationToProcessBuf = frameAnimationToProcessGB.LockBufferForWrite(0, (int)rad.frameAnimationToProcessCounter);
|
||||
var rigAnimationJobsBuf = frameRigAnimationJobsGB.LockBufferForWrite(0, (int)rad.frameAnimatedRigsCounter);
|
||||
|
||||
// Fill GPU buffer data for current frame animation calculation
|
||||
var fillFrameAnimatedRigWorkloadDataJob = new FillFrameAnimatedRigWorkloadBuffersJob()
|
||||
{
|
||||
animatedBonesWorkloadBuf = animatedBonesWorkloadBuf,
|
||||
animationToProcessBuf = animationToProcessBuf,
|
||||
frameRigAnimationJobs = rigAnimationJobsBuf,
|
||||
animationClipsOffsets = rad.animationClipsMap,
|
||||
rigDefinitionOffsets = rad.rigDefinitionsMap,
|
||||
avatarMasksOffsets = rad.avatarMasksDataMap,
|
||||
frameOffsetsTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>(true),
|
||||
atpBufTypeHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>(true),
|
||||
rigDefComponentTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true),
|
||||
entityTypeHandle = SystemAPI.GetEntityTypeHandle()
|
||||
};
|
||||
fillFrameAnimatedRigWorkloadDataJob.ScheduleParallel(gpuAnimatedRigQuery, new JobHandle()).Complete();
|
||||
|
||||
framePerBoneAnimationWorkloadGB.UnlockBufferAfterWrite((int)rad.frameAnimatedBonesCounter);
|
||||
frameAnimationToProcessGB.UnlockBufferAfterWrite((int)rad.frameAnimationToProcessCounter);
|
||||
frameRigAnimationJobsGB.UnlockBufferAfterWrite((int)rad.frameAnimatedRigsCounter);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle ResetFrameData(ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var resetFrameDataJob = new ResetFrameDataJob()
|
||||
{
|
||||
frameAnimatedBonesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimatedBonesCounter)),
|
||||
frameAnimatedRigsCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimatedRigsCounter)),
|
||||
frameSkinnedMeshesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter)),
|
||||
frameAnimationToProcessCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimationToProcessCounter)),
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters
|
||||
};
|
||||
var rv = resetFrameDataJob.Schedule(dependsOn);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PrepareFrameAnimationData(ref GPURuntimeAnimationData runtimeAnimationData, JobHandle dependsOn)
|
||||
{
|
||||
var resetFrameDataJH = ResetFrameData(ref runtimeAnimationData, dependsOn);
|
||||
var calculateFrameWorksetDataJH = CalculateFrameWorksetData(ref runtimeAnimationData, resetFrameDataJH);
|
||||
|
||||
// Complete dependency because need to resize GPU buffers with actualized frame counts
|
||||
calculateFrameWorksetDataJH.Complete();
|
||||
|
||||
ResizeGPUBuffers(ref runtimeAnimationData);
|
||||
CopyPersistentAnimationDataToGPUBuffers(ref runtimeAnimationData);
|
||||
GPUBuffersBeginFrame();
|
||||
FillFrameAnimationCalculationWorkloadGPUBuffers(ref runtimeAnimationData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyPersistentAnimationDataToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
CopyNewAnimationsToGPUBuffers(ref rad);
|
||||
CopyNewRigsToGPUBuffer(ref rad);
|
||||
CopyNewBoneRemapTablesToGPUBuffers(ref rad);
|
||||
CopyNewAvatarMasksToGPUBuffer(ref rad);
|
||||
//DebugBufRead(ref rad);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GraphicsBuffer GrowPersistentDataComputeBuffer(GraphicsBuffer gb, uint newElementCount)
|
||||
{
|
||||
if (gb == null || gb.count >= newElementCount)
|
||||
return gb;
|
||||
|
||||
var newBuf = new GraphicsBuffer(gb.target, gb.usageFlags, (int)newElementCount, gb.stride);
|
||||
ComputeBufferTools.Copy(gb, newBuf, 0, 0, (uint)(newElementCount * gb.stride));
|
||||
gb.Dispose();
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ResizeGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
// Persistent data buffers
|
||||
animationClipsGB = GrowPersistentDataComputeBuffer(animationClipsGB, rad.totalGPUAnimationClipsSize.x / 4);
|
||||
avatarMasksGB = GrowPersistentDataComputeBuffer(avatarMasksGB, rad.totalGPUAvatarMasksDataCount.x);
|
||||
rigDefinitionGB = GrowPersistentDataComputeBuffer(rigDefinitionGB, rad.totalGPURigsCount.x);
|
||||
rigHumanAvatarRotationDataGB = GrowPersistentDataComputeBuffer(rigHumanAvatarRotationDataGB, rad.totalGPUHumanRotationDataEntriesCount.x);
|
||||
rigBonesGB = GrowPersistentDataComputeBuffer(rigBonesGB, rad.totalGPURigBonesCount.x);
|
||||
skinnedMeshBoneDataGB = GrowPersistentDataComputeBuffer(skinnedMeshBoneDataGB, rad.totalGPUSkinnedMeshBonesCount.x);
|
||||
|
||||
// Everyframe updated buffers
|
||||
frameAnimationToProcessGB.Grow((int)rad.frameAnimationToProcessCounter);
|
||||
framePerBoneAnimationWorkloadGB.Grow((int)rad.frameAnimatedBonesCounter);
|
||||
frameRigAnimationJobsGB.Grow((int)rad.frameAnimatedRigsCounter);
|
||||
frameSkinMatrixWorkloadGB.Grow((int)rad.frameSkinnedMeshesCounter);
|
||||
|
||||
animatedBonesGB = ComputeBufferTools.GrowNoCopy(animatedBonesGB, (int)rad.frameAnimatedBonesCounter);
|
||||
rigSpaceAnimatedBonesGB = ComputeBufferTools.GrowNoCopy(rigSpaceAnimatedBonesGB, (int)rad.frameAnimatedBonesCounter);
|
||||
Shader.SetGlobalBuffer(ShaderID_rigSpaceBoneTransformsBuf, rigSpaceAnimatedBonesGB);
|
||||
Shader.SetGlobalBuffer(ShaderID_boneLocalTransforms, animatedBonesGB);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewBoneRemapTablesToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newSkinnedMeshesDataList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newSkinnedMeshBonesCount = (int)(rad.totalGPUSkinnedMeshBonesCount.x - rad.totalGPUSkinnedMeshBonesCount.y);
|
||||
|
||||
var skinnedMeshBonesUploader = sparseUploaderPool.GetUploader(skinnedMeshBoneDataGB);
|
||||
var skinnedMeshBonesUploadSizeInBytes = newSkinnedMeshBonesCount * UnsafeUtility.SizeOf<GPUStructures.SkinnedMeshBoneData>();
|
||||
var skinnedMeshBonesThreadedUploader = skinnedMeshBonesUploader.Begin(skinnedMeshBonesUploadSizeInBytes, skinnedMeshBonesUploadSizeInBytes, newSkinnedMeshBonesCount);
|
||||
|
||||
var copyNewBoneRemapTables = new CopyNewSkinnedMeshesDataJob()
|
||||
{
|
||||
newSkinnedMeshData = rad.newSkinnedMeshesDataList,
|
||||
gpuSkinnedMeshBonesData = skinnedMeshBonesThreadedUploader,
|
||||
};
|
||||
|
||||
copyNewBoneRemapTables.ScheduleParallel(rad.newSkinnedMeshesDataList.Length, 1, default).Complete();
|
||||
|
||||
skinnedMeshBonesUploader.EndAndCommit(skinnedMeshBonesThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(skinnedMeshBonesUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewAvatarMasksToGPUBuffer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newAvatarMasksList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newAvatarMasksEntriesCount = (int)(rad.totalGPUAvatarMasksDataCount.x - rad.totalGPUAvatarMasksDataCount.y);
|
||||
|
||||
var avatarMaskUploader = sparseUploaderPool.GetUploader(avatarMasksGB);
|
||||
var avatarMaskUploadSizeInBytes = newAvatarMasksEntriesCount * UnsafeUtility.SizeOf<uint>();
|
||||
var avatarMaskThreadedUploader = avatarMaskUploader.Begin(avatarMaskUploadSizeInBytes, avatarMaskUploadSizeInBytes, newAvatarMasksEntriesCount);
|
||||
|
||||
var copyNewAvatarMasksToGPUJob = new CopyNewAvatarMasksToGPUJob()
|
||||
{
|
||||
gpuAvatarMasks = avatarMaskThreadedUploader,
|
||||
newAvatarMasks = rad.newAvatarMasksList
|
||||
};
|
||||
|
||||
copyNewAvatarMasksToGPUJob.ScheduleParallel(rad.newAvatarMasksList.Length, 1, default).Complete();
|
||||
|
||||
avatarMaskUploader.EndAndCommit(avatarMaskThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(avatarMaskUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewRigsToGPUBuffer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newRigsList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newRigBonesCount = (int)(rad.totalGPURigBonesCount.x - rad.totalGPURigBonesCount.y);
|
||||
var newRigsCount = (int)(rad.totalGPURigsCount.x - rad.totalGPURigsCount.y);
|
||||
var newHumanRotationDataEntriesCount = (int)(rad.totalGPUHumanRotationDataEntriesCount.x - rad.totalGPUHumanRotationDataEntriesCount.y);
|
||||
|
||||
var rigBonesUploader = sparseUploaderPool.GetUploader(rigBonesGB);
|
||||
var rigBonesUploadSizeInBytes = newRigBonesCount * UnsafeUtility.SizeOf<GPUStructures.RigBone>();
|
||||
var rigBonesThreadedUploader = rigBonesUploader.Begin(rigBonesUploadSizeInBytes, rigBonesUploadSizeInBytes, newRigBonesCount);
|
||||
|
||||
var rigDefinitionUploader = sparseUploaderPool.GetUploader(rigDefinitionGB);
|
||||
var rigDefinitionsUploadSizeInBytes = newRigsCount * UnsafeUtility.SizeOf<GPUStructures.RigDefinition>();
|
||||
var rigDefinitionsThreadedUploader = rigDefinitionUploader.Begin(rigDefinitionsUploadSizeInBytes, rigDefinitionsUploadSizeInBytes, newRigsCount);
|
||||
|
||||
var rigHumanRotationDataUploader = sparseUploaderPool.GetUploader(rigHumanAvatarRotationDataGB);
|
||||
var rigHumanRotationDataUploadSizeInBytes = newHumanRotationDataEntriesCount * UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>();
|
||||
var rigHumanRotationDataThreadedUploader = rigHumanRotationDataUploader.Begin(rigHumanRotationDataUploadSizeInBytes, rigHumanRotationDataUploadSizeInBytes, newRigsCount);
|
||||
|
||||
var copyNewRigsToGPUJob = new CopyNewRigsToGPUJob()
|
||||
{
|
||||
newRigs = rad.newRigsList,
|
||||
gpuRigBones = rigBonesThreadedUploader,
|
||||
gpuRigDefs = rigDefinitionsThreadedUploader,
|
||||
gpuHumanRotationData = rigHumanRotationDataThreadedUploader
|
||||
};
|
||||
|
||||
copyNewRigsToGPUJob.ScheduleParallel(rad.newRigsList.Length, 1, default).Complete();
|
||||
|
||||
rigDefinitionUploader.EndAndCommit(rigDefinitionsThreadedUploader);
|
||||
rigBonesUploader.EndAndCommit(rigBonesThreadedUploader);
|
||||
rigHumanRotationDataUploader.EndAndCommit(rigHumanRotationDataThreadedUploader);
|
||||
|
||||
sparseUploaderPool.PutUploader(rigDefinitionUploader);
|
||||
sparseUploaderPool.PutUploader(rigBonesUploader);
|
||||
sparseUploaderPool.PutUploader(rigHumanRotationDataUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewAnimationsToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newAnimationsList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newAnimationClipsCount = rad.newAnimationsList.Length;
|
||||
// Very conservative value
|
||||
var maxNumUploadsPerClip = 0xf;
|
||||
|
||||
var animationClipsUploader = sparseUploaderPool.GetUploader(animationClipsGB);
|
||||
var maxAnimationClipSingleUploadSize = rad.totalGPUAnimationClipsSize.x - rad.totalGPUAnimationClipsSize.y;
|
||||
var animationClipsThreadedUploader = animationClipsUploader.Begin((int)maxAnimationClipSingleUploadSize, (int)maxAnimationClipSingleUploadSize, newAnimationClipsCount * maxNumUploadsPerClip);
|
||||
|
||||
var createGPUAnimationClip = new CopyNewAnimationsToGPUJob()
|
||||
{
|
||||
newAnimationClips = rad.newAnimationsList,
|
||||
gpuAnimationClips = animationClipsThreadedUploader,
|
||||
};
|
||||
|
||||
createGPUAnimationClip.ScheduleParallel(rad.newAnimationsList.Length, 1, default).Complete();
|
||||
|
||||
animationClipsUploader.EndAndCommit(animationClipsThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(animationClipsUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal void BuildSkinMatrices(in NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap, FrameFencedGPUBufferPool<SkinMatrix> outSkinMatrixGB)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<GPURuntimeAnimationData>(out var radRW))
|
||||
return;
|
||||
|
||||
ref var runtimeAnimationData = ref radRW.ValueRW;
|
||||
|
||||
frameSkinMatrixWorkloadGB.BeginFrame();
|
||||
|
||||
FillFrameSkinMatrixCalculationWorkloadGPUBuffers(ref runtimeAnimationData, entityToSMRFrameDataMap);
|
||||
|
||||
if (runtimeAnimationData.frameSkinnedMeshesCounter > 0)
|
||||
{
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_skinMatrixWorkloadBuf, frameSkinMatrixWorkloadGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_rigSpaceBoneTransformsBuf, rigSpaceAnimatedBonesGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_outSkinMatrices, outSkinMatrixGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_skinnedMeshBoneData, skinnedMeshBoneDataGB);
|
||||
animationEngineCS.SetInt(ShaderID_totalSkinnedMeshes, (int)runtimeAnimationData.frameSkinnedMeshesCounter);
|
||||
|
||||
makeSkinMatricesKernel.Dispatch(runtimeAnimationData.frameSkinnedMeshesCounter, 1, 1);
|
||||
}
|
||||
|
||||
frameSkinMatrixWorkloadGB.EndFrame();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DispatchAnimationCalculation(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.frameAnimatedBonesCounter == 0)
|
||||
return;
|
||||
|
||||
// Animation computation
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_outAnimatedBones, animatedBonesGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animatedBoneWorkload, framePerBoneAnimationWorkloadGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationJobs, frameRigAnimationJobsGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationsToProcess, frameAnimationToProcessGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_rigDefinitions, rigDefinitionGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_rigBones, rigBonesGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationClips, animationClipsGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_humanRotationDataBuffer, rigHumanAvatarRotationDataGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_avatarMasksBuffer, avatarMasksGB);
|
||||
animationEngineCS.SetInt(ShaderID_animatedBonesCount, (int)rad.frameAnimatedBonesCounter);
|
||||
|
||||
processAnimationKernel.Dispatch(rad.frameAnimatedBonesCounter, 1, 1);
|
||||
|
||||
// Compute rig space bone transforms
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_boneLocalTransforms, animatedBonesGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_animatedBoneWorkload, framePerBoneAnimationWorkloadGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_animationJobs, frameRigAnimationJobsGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_rigDefinitions, rigDefinitionGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_rigBones, rigBonesGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_outBoneTransforms, rigSpaceAnimatedBonesGB);
|
||||
makeRigSpaceBoneTransformsKernel.Dispatch(rad.frameAnimatedBonesCounter, 1, 1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SetBuffersForBoneRenderer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
boneVisualizationSystem ??= World.GetExistingSystemManaged<BoneVisualizationSystem>();
|
||||
boneVisualizationSystem.gpuBoneRenderer.SetGPUBuffersForFrame
|
||||
(
|
||||
framePerBoneAnimationWorkloadGB,
|
||||
frameRigAnimationJobsGB,
|
||||
rigDefinitionGB,
|
||||
rigBonesGB,
|
||||
rigSpaceAnimatedBonesGB,
|
||||
(int)rad.frameAnimatedBonesCounter
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user