From 2d0dcdb6ab4563d91fcc21cd17cd22a5de6710d1 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Molteni Date: Mon, 23 Sep 2024 07:03:54 -0300 Subject: [PATCH] chore: batch update Avatar skinning matrices (#1855) * First working version * Using unsafe pointers * Proof of concept is valid! * Removed unnecesasry cleanup, pass the NativeArray entirely and read by index, some clenaup * Reuse and resize strategy * Added NetworkAvatar property * Delete avatar transform matrix * Added test suite * Naming convetions for testing * Moved FinshAvatarMatricesCaluclation to PreRenderingSystemGroup * Applied suggestions * Some cleanup * Further merge --------- Co-authored-by: Mikhail Agapov <118179774+mikhail-dcl@users.noreply.github.com> --- .../AvatarShape/AvatarShape.asmdef | 3 +- .../AvatarTransformMatrixComponent.cs | 53 +----- .../AvatarTransformMatrixJobWrapper.cs | 156 ++++++++++++++++++ .../AvatarTransformMatrixJobWrapper.cs.meta | 3 + .../ComputeShader/BoneMatrixCalculationJob.cs | 24 ++- .../ComputeShader/ComputeShaderSkinning.cs | 5 +- .../ComputeShader/CustomSkinning.cs | 2 +- .../AvatarShape/Helpers/ReleaseAvatar.cs | 9 +- .../Systems/AvatarCleanUpSystem.cs | 14 +- .../Systems/AvatarInstantiatorSystem.cs | 16 +- .../FinishAvatarMatricesCalculationSystem.cs | 18 +- .../StartAvatarMatricesCalculationSystem.cs | 15 +- .../Systems/TrackTransformMatrixSystem.cs | 55 ------ .../TrackTransformMatrixSystem.cs.meta | 3 - .../AvatarTransformMatrixJobWrapperShould.cs | 108 ++++++++++++ ...tarTransformMatrixJobWrapperShould.cs.meta | 11 ++ .../AvatarInstantiatorSystemShould.cs | 3 +- .../AvatarShape/UnityInterface/AvatarBase.cs | 5 + .../Systems/InstantiateRandomAvatarsSystem.cs | 86 ++++++---- .../CalculateCharacterVelocitySystem.cs | 2 + .../Systems/CharacterPlatformSystem.cs | 3 +- .../Systems/InterpolateCharacterSystem.cs | 4 +- .../Systems/StunCharacterSystem.cs | 2 + .../DCL/PluginSystem/Global/AvatarPlugin.cs | 20 ++- 24 files changed, 453 insertions(+), 167 deletions(-) create mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs create mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs.meta delete mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs delete mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs.meta create mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs create mode 100644 Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs.meta diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/AvatarShape.asmdef b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/AvatarShape.asmdef index 4c44ac7c1b..b136582298 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/AvatarShape.asmdef +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/AvatarShape.asmdef @@ -25,7 +25,8 @@ "GUID:8322ea9340a544c59ddc56d4793eac74", "GUID:166b65e6dfc848bb9fb075f53c293a38", "GUID:e25ef972de004615a22937e739de2def", - "GUID:543b8f091a5947a3880b7f2bca2358bd" + "GUID:543b8f091a5947a3880b7f2bca2358bd", + "GUID:d832748739a186646b8656bdbd447ad0" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixComponent.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixComponent.cs index 9cd08167f9..706021366e 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixComponent.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixComponent.cs @@ -8,55 +8,18 @@ namespace DCL.AvatarRendering.AvatarShape.Components { - public struct AvatarTransformMatrixComponent : IDisposable + public struct AvatarTransformMatrixComponent { - private TransformAccessArray bones; - private BoneMatrixCalculationJob job; - private JobHandle handle; + public int IndexInGlobalJobArray; + public Transform[] bones; - internal bool disposed { get; private set; } - - internal bool completed { get; private set; } - - public void ScheduleBoneMatrixCalculation(Matrix4x4 avatarWorldToLocalMatrix) - { - if (disposed) - throw new ObjectDisposedException(nameof(AvatarTransformMatrixComponent), $"{nameof(ScheduleBoneMatrixCalculation)} called on the disposed component"); - - if (!handle.IsCompleted) - return; - - job.AvatarTransform = avatarWorldToLocalMatrix; - handle = job.Schedule(bones); - completed = false; - } - - public NativeArray CompleteBoneMatrixCalculations() + public static AvatarTransformMatrixComponent Create(Transform[] bones) { - handle.Complete(); - completed = true; - return job.BonesMatricesResult; - } - - public void Dispose() - { - handle.Complete(); - job.BonesMatricesResult.Dispose(); - bones.Dispose(); - - disposed = true; - completed = true; - } - - public static AvatarTransformMatrixComponent Create(Transform avatarBaseTransform, Transform[] bones) => - new () + return new AvatarTransformMatrixComponent { - bones = new TransformAccessArray(bones), - job = new BoneMatrixCalculationJob - { - BonesMatricesResult = new NativeArray(bones.Length, Allocator.Persistent), - AvatarTransform = avatarBaseTransform.worldToLocalMatrix, - }, + IndexInGlobalJobArray = -1, + bones = bones }; + } } } diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs new file mode 100644 index 0000000000..a790790f41 --- /dev/null +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DCL.AvatarRendering.AvatarShape.ComputeShader; +using DCL.AvatarRendering.AvatarShape.UnityInterface; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine; +using UnityEngine.Jobs; + +namespace DCL.AvatarRendering.AvatarShape.Components +{ + public unsafe class AvatarTransformMatrixJobWrapper : IDisposable + { + internal const int AVATAR_ARRAY_SIZE = 100; + private static readonly int BONES_ARRAY_LENGTH = ComputeShaderConstants.BONE_COUNT; + private static readonly int BONES_PER_AVATAR_LENGTH = AVATAR_ARRAY_SIZE * BONES_ARRAY_LENGTH; + + internal NativeArray matrixFromAllAvatars; + private Matrix4x4* matrixPtr; + + internal NativeArray updateAvatar; + private bool* updateAvatarPtr; + + private TransformAccessArray bonesCombined; + public BoneMatrixCalculationJob job; + + private JobHandle handle; + + private readonly Stack releasedIndexes; + + private int avatarIndex; + private int nextResizeValue; + internal int currentAvatarAmountSupported; + + public AvatarTransformMatrixJobWrapper() + { + job = new BoneMatrixCalculationJob(BONES_ARRAY_LENGTH, BONES_PER_AVATAR_LENGTH); + + bonesCombined = new TransformAccessArray(BONES_PER_AVATAR_LENGTH); + for (int i = 0; i < BONES_PER_AVATAR_LENGTH; i++) + bonesCombined.Add(null); + + matrixFromAllAvatars + = new NativeArray(AVATAR_ARRAY_SIZE, Allocator.Persistent); + matrixPtr = (Matrix4x4*)matrixFromAllAvatars.GetUnsafePtr(); + + updateAvatar = new NativeArray(AVATAR_ARRAY_SIZE, Allocator.Persistent); + updateAvatarPtr = (bool*)updateAvatar.GetUnsafePtr(); + + currentAvatarAmountSupported = AVATAR_ARRAY_SIZE; + + nextResizeValue = 2; + releasedIndexes = new Stack(); + } + + + public void ScheduleBoneMatrixCalculation() + { + job.AvatarTransform = matrixFromAllAvatars; + job.UpdateAvatar = updateAvatar; + handle = job.Schedule(bonesCombined); + } + + public void CompleteBoneMatrixCalculations() + { + handle.Complete(); + } + + public void UpdateAvatar(AvatarBase avatarBase, ref AvatarTransformMatrixComponent transformMatrixComponent) + { + if (transformMatrixComponent.IndexInGlobalJobArray == -1) + { + if (releasedIndexes.Count > 0) + transformMatrixComponent.IndexInGlobalJobArray = releasedIndexes.Pop(); + else + { + transformMatrixComponent.IndexInGlobalJobArray = avatarIndex; + avatarIndex++; + } + + //Add all bones to the bonesCombined array with the current available index + for (int i = 0; i < BONES_ARRAY_LENGTH; i++) + bonesCombined[transformMatrixComponent.IndexInGlobalJobArray * BONES_ARRAY_LENGTH + i] = + transformMatrixComponent.bones[i]; + } + + //Setup of data + matrixPtr[transformMatrixComponent.IndexInGlobalJobArray] = avatarBase.transform.worldToLocalMatrix; + updateAvatarPtr[transformMatrixComponent.IndexInGlobalJobArray] = true; + + if (avatarIndex >= currentAvatarAmountSupported - 1) + ResizeArrays(); + } + + private void ResizeArrays() + { + var newBonesCombined + = new TransformAccessArray(BONES_PER_AVATAR_LENGTH * nextResizeValue); + for (var i = 0; i < BONES_PER_AVATAR_LENGTH * nextResizeValue; i++) + { + if (i < BONES_PER_AVATAR_LENGTH * (nextResizeValue - 1)) + newBonesCombined.Add(bonesCombined[i]); + else + newBonesCombined.Add(null); + } + + bonesCombined.Dispose(); + bonesCombined = newBonesCombined; + + var newMatrixFromAllAvatars + = new NativeArray(AVATAR_ARRAY_SIZE * nextResizeValue, Allocator.Persistent); + UnsafeUtility.MemCpy(newMatrixFromAllAvatars.GetUnsafePtr(), matrixFromAllAvatars.GetUnsafePtr(), + matrixFromAllAvatars.Length * sizeof(Matrix4x4)); + matrixFromAllAvatars.Dispose(); + matrixFromAllAvatars = newMatrixFromAllAvatars; + matrixPtr = (Matrix4x4*)matrixFromAllAvatars.GetUnsafePtr(); + + var newUpdateAvatar + = new NativeArray(AVATAR_ARRAY_SIZE * nextResizeValue, Allocator.Persistent); + UnsafeUtility.MemCpy(newUpdateAvatar.GetUnsafePtr(), updateAvatar.GetUnsafePtr(), + updateAvatar.Length * sizeof(bool)); + updateAvatar.Dispose(); + updateAvatar = newUpdateAvatar; + updateAvatarPtr = (bool*)updateAvatar.GetUnsafePtr(); + + job.BonesMatricesResult.Dispose(); + job = new BoneMatrixCalculationJob(BONES_ARRAY_LENGTH, BONES_PER_AVATAR_LENGTH * nextResizeValue); + + currentAvatarAmountSupported = AVATAR_ARRAY_SIZE * nextResizeValue; + nextResizeValue++; + } + + public void Dispose() + { + handle.Complete(); + bonesCombined.Dispose(); + updateAvatar.Dispose(); + job.BonesMatricesResult.Dispose(); + } + + public void ReleaseAvatar(ref AvatarTransformMatrixComponent avatarTransformMatrixComponent) + { + if (avatarTransformMatrixComponent.IndexInGlobalJobArray == -1) + return; + + //Dont update this index anymore until reset + updateAvatarPtr[avatarTransformMatrixComponent.IndexInGlobalJobArray] = false; + releasedIndexes.Push(avatarTransformMatrixComponent.IndexInGlobalJobArray); + + avatarTransformMatrixComponent.IndexInGlobalJobArray = -1; + } + } +} \ No newline at end of file diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs.meta b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs.meta new file mode 100644 index 0000000000..9bb6745e1e --- /dev/null +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Components/AvatarTransformMatrixJobWrapper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c2870dc6eaf64d27a62bc3cd13331080 +timeCreated: 1724172117 \ No newline at end of file diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/BoneMatrixCalculationJob.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/BoneMatrixCalculationJob.cs index f76f95990b..1008fb44c9 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/BoneMatrixCalculationJob.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/BoneMatrixCalculationJob.cs @@ -9,12 +9,32 @@ namespace DCL.AvatarRendering.AvatarShape.ComputeShader [BurstCompile] public struct BoneMatrixCalculationJob : IJobParallelForTransform { + private readonly int BONE_COUNT; + private int AvatarIndex; + public NativeArray BonesMatricesResult; - public Matrix4x4 AvatarTransform; + [NativeDisableParallelForRestriction] + public NativeArray AvatarTransform; + + [NativeDisableParallelForRestriction] public NativeArray UpdateAvatar; + + public BoneMatrixCalculationJob(int boneCount, int bonesPerAvatarLength) + { + BONE_COUNT = boneCount; + BonesMatricesResult = new NativeArray(bonesPerAvatarLength, Allocator.Persistent); + AvatarTransform = default; + UpdateAvatar = default; + AvatarIndex = 0; + } public void Execute(int index, TransformAccess transform) { - BonesMatricesResult[index] = AvatarTransform * transform.localToWorldMatrix; + // The avatarIndex is calculated by dividing the index by the amount of bones per avatar + // Therefore, all of the indexes between 0 and ComputeShaderConstants.BONE_COUNT correlates to a single avatar + AvatarIndex = index / BONE_COUNT; + if (!UpdateAvatar[AvatarIndex]) + return; + BonesMatricesResult[index] = AvatarTransform[AvatarIndex] * transform.localToWorldMatrix; } } } diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/ComputeShaderSkinning.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/ComputeShaderSkinning.cs index f04354f4a7..67f22d0bd8 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/ComputeShaderSkinning.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/ComputeShaderSkinning.cs @@ -37,9 +37,10 @@ public override AvatarCustomSkinningComponent Initialize(IList return new AvatarCustomSkinningComponent(vertCount, buffers, materialSetups, skinningShader, totalBounds); } - public override void ComputeSkinning(NativeArray bonesResult, ref AvatarCustomSkinningComponent skinning) + + public override void ComputeSkinning(NativeArray bonesResult, int indexInGlobalResultArray, ref AvatarCustomSkinningComponent skinning) { - skinning.buffers.bones.SetData(bonesResult); + skinning.buffers.bones.SetData(bonesResult, indexInGlobalResultArray * ComputeShaderConstants.BONE_COUNT, 0 , ComputeShaderConstants.BONE_COUNT); skinning.computeShaderInstance.Dispatch(skinning.buffers.kernel, (skinning.vertCount / 64) + 1, 1, 1); //Note (Juani): According to Unity, BeginWrite/EndWrite works better than SetData. But we got inconsitent result using ComputeBufferMode.SubUpdates diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/CustomSkinning.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/CustomSkinning.cs index dbec41a220..c42ecb15d3 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/CustomSkinning.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/ComputeShader/CustomSkinning.cs @@ -18,7 +18,7 @@ public abstract AvatarCustomSkinningComponent Initialize(IList UnityEngine.ComputeShader skinningShader, IAvatarMaterialPoolHandler avatarMaterial, AvatarShapeComponent avatarShapeComponent, in FacialFeaturesTextures facialFeatureTexture); - public abstract void ComputeSkinning(NativeArray bonesResult, ref AvatarCustomSkinningComponent skinning); + public abstract void ComputeSkinning(NativeArray bonesResult, int indexInGlobalResultArray, ref AvatarCustomSkinningComponent skinning); private protected abstract AvatarCustomSkinningComponent.MaterialSetup SetupMaterial(Renderer meshRenderer, Material originalMaterial, int lastWearableVertCount, IAvatarMaterialPoolHandler celShadingMaterial, AvatarShapeComponent shapeComponent, in FacialFeaturesTextures facialFeaturesTextures); diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Helpers/ReleaseAvatar.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Helpers/ReleaseAvatar.cs index fdbdf3f9ed..f46dd71344 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Helpers/ReleaseAvatar.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Helpers/ReleaseAvatar.cs @@ -9,12 +9,17 @@ namespace DCL.AvatarRendering.AvatarShape.Helpers public static class ReleaseAvatar { public static void Execute(FixedComputeBufferHandler vertOutBuffer, IAttachmentsAssetsCache wearableAssetsCache, - IAvatarMaterialPoolHandler avatarMaterialPoolHandler, IObjectPool computeShaderSkinningPool, - in AvatarShapeComponent avatarShapeComponent, ref AvatarCustomSkinningComponent skinningComponent) + IAvatarMaterialPoolHandler avatarMaterialPoolHandler, + IObjectPool computeShaderSkinningPool, + in AvatarShapeComponent avatarShapeComponent, ref AvatarCustomSkinningComponent skinningComponent, + ref AvatarTransformMatrixComponent avatarTransformMatrixComponent, + AvatarTransformMatrixJobWrapper jobWrapper) { vertOutBuffer.Release(skinningComponent.VertsOutRegion); skinningComponent.Dispose(avatarMaterialPoolHandler, computeShaderSkinningPool); + jobWrapper.ReleaseAvatar(ref avatarTransformMatrixComponent); + if (avatarShapeComponent.WearablePromise.IsConsumed) wearableAssetsCache.ReleaseAssets(avatarShapeComponent.InstantiatedWearables); else diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarCleanUpSystem.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarCleanUpSystem.cs index 8d5eb47aff..2c5a3251f0 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarCleanUpSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarCleanUpSystem.cs @@ -33,6 +33,9 @@ public partial class AvatarCleanUpSystem : BaseUnityLoopSystem private readonly IObjectPool computeShaderSkinningPool; private readonly IAttachmentsAssetsCache wearableAssetsCache; + private readonly AvatarTransformMatrixJobWrapper avatarTransformMatrixBatchJob; + + internal AvatarCleanUpSystem( World world, IPerformanceBudget instantiationFrameTimeBudget, @@ -41,7 +44,8 @@ internal AvatarCleanUpSystem( IComponentPool avatarPoolRegistry, IObjectPool computeShaderSkinningPool, IAttachmentsAssetsCache wearableAssetsCache, - ObjectProxy mainPlayerAvatarBaseProxy) : base(world) + ObjectProxy mainPlayerAvatarBaseProxy, + AvatarTransformMatrixJobWrapper avatarTransformMatrixBatchJob) : base(world) { this.instantiationFrameTimeBudget = instantiationFrameTimeBudget; this.vertOutBuffer = vertOutBuffer; @@ -50,6 +54,7 @@ internal AvatarCleanUpSystem( this.computeShaderSkinningPool = computeShaderSkinningPool; this.wearableAssetsCache = wearableAssetsCache; this.mainPlayerAvatarBaseProxy = mainPlayerAvatarBaseProxy; + this.avatarTransformMatrixBatchJob = avatarTransformMatrixBatchJob; } protected override void Update(float t) @@ -85,7 +90,7 @@ private void DestroyAvatar(ref AvatarShapeComponent avatarShapeComponent, ref Av deleteEntityIntention.DeferDeletion = true; return; } - + InternalDestroyAvatar(ref avatarShapeComponent, ref skinningComponent, ref avatarTransformMatrixComponent, avatarBase); deleteEntityIntention.DeferDeletion = false; } @@ -97,9 +102,10 @@ private void InternalDestroyAvatar(ref AvatarShapeComponent avatarShapeComponent if (mainPlayerAvatarBaseProxy.Object == avatarBase) mainPlayerAvatarBaseProxy.ReleaseObject(); - ReleaseAvatar.Execute(vertOutBuffer, wearableAssetsCache, avatarMaterialPoolHandler, computeShaderSkinningPool, avatarShapeComponent, ref skinningComponent); + ReleaseAvatar.Execute(vertOutBuffer, wearableAssetsCache, avatarMaterialPoolHandler, + computeShaderSkinningPool, avatarShapeComponent, ref skinningComponent, + ref avatarTransformMatrixComponent, avatarTransformMatrixBatchJob); - avatarTransformMatrixComponent.Dispose(); avatarPoolRegistry.Release(avatarBase); } } diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarInstantiatorSystem.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarInstantiatorSystem.cs index f1ee39d4a3..7ae64dd5d1 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarInstantiatorSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/AvatarInstantiatorSystem.cs @@ -50,11 +50,14 @@ public partial class AvatarInstantiatorSystem : BaseUnityLoopSystem private readonly IWearableStorage wearableStorage; private readonly IWearable?[] fallbackBodyShape = new IWearable[1]; + private readonly AvatarTransformMatrixJobWrapper avatarTransformMatrixBatchJob; + + public AvatarInstantiatorSystem(World world, IPerformanceBudget instantiationFrameTimeBudget, IPerformanceBudget memoryBudget, IComponentPool avatarPoolRegistry, IAvatarMaterialPoolHandler avatarMaterialPoolHandler, IObjectPool computeShaderPool, IAttachmentsAssetsCache wearableAssetsCache, CustomSkinning skinningStrategy, FixedComputeBufferHandler vertOutBuffer, ObjectProxy mainPlayerAvatarBaseProxy, IDefaultFaceFeaturesHandler defaultFaceFeaturesHandler, - IWearableStorage wearableStorage) : base(world) + IWearableStorage wearableStorage, AvatarTransformMatrixJobWrapper avatarTransformMatrixBatchJob) : base(world) { this.instantiationFrameTimeBudget = instantiationFrameTimeBudget; this.avatarPoolRegistry = avatarPoolRegistry; @@ -68,6 +71,7 @@ public AvatarInstantiatorSystem(World world, IPerformanceBudget instantiationFra this.mainPlayerAvatarBaseProxy = mainPlayerAvatarBaseProxy; this.defaultFaceFeaturesHandler = defaultFaceFeaturesHandler; this.wearableStorage = wearableStorage; + this.avatarTransformMatrixBatchJob = avatarTransformMatrixBatchJob; } public override void Dispose() @@ -112,7 +116,8 @@ protected override void Update(float t) avatarTransform.ResetLocalTRS(); - var avatarTransformMatrixComponent = AvatarTransformMatrixComponent.Create(avatarBase.transform, avatarBase.AvatarSkinnedMeshRenderer.bones); + var avatarTransformMatrixComponent = + AvatarTransformMatrixComponent.Create(avatarBase.AvatarSkinnedMeshRenderer.bones); AvatarCustomSkinningComponent skinningComponent = InstantiateAvatar(ref avatarShapeComponent, in wearablesResult, avatarBase); @@ -135,14 +140,17 @@ private void InstantiateMainPlayerAvatar(in Entity entity, ref AvatarShapeCompon [All(typeof(CharacterTransform))] [None(typeof(DeleteEntityIntention))] private void InstantiateExistingAvatar(ref AvatarShapeComponent avatarShapeComponent, AvatarBase avatarBase, - ref AvatarCustomSkinningComponent skinningComponent) + ref AvatarCustomSkinningComponent skinningComponent, + ref AvatarTransformMatrixComponent avatarTransformMatrixComponent) { if (!ReadyToInstantiateNewAvatar(ref avatarShapeComponent)) return; if (!avatarShapeComponent.WearablePromise.SafeTryConsume(World, GetReportCategory(), out WearablesLoadResult wearablesResult)) return; if (!avatarShapeComponent.EmotePromise.SafeTryConsume(World, GetReportCategory(), out EmotesLoadResult emotesResult)) return; - ReleaseAvatar.Execute(vertOutBuffer, wearableAssetsCache, avatarMaterialPoolHandler, computeShaderSkinningPool, avatarShapeComponent, ref skinningComponent); + ReleaseAvatar.Execute(vertOutBuffer, wearableAssetsCache, avatarMaterialPoolHandler, + computeShaderSkinningPool, avatarShapeComponent, ref skinningComponent, + ref avatarTransformMatrixComponent, avatarTransformMatrixBatchJob); // Override by ref skinningComponent = InstantiateAvatar(ref avatarShapeComponent, in wearablesResult, avatarBase); diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/FinishAvatarMatricesCalculationSystem.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/FinishAvatarMatricesCalculationSystem.cs index 46ba20cc8f..eab900d893 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/FinishAvatarMatricesCalculationSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/FinishAvatarMatricesCalculationSystem.cs @@ -1,27 +1,35 @@ using Arch.Core; using Arch.System; using Arch.SystemGroups; +using Arch.SystemGroups.DefaultSystemGroups; using DCL.AvatarRendering.AvatarShape.Components; using DCL.AvatarRendering.AvatarShape.ComputeShader; using ECS.Abstract; using ECS.LifeCycle.Components; +using Unity.Collections; +using Unity.Mathematics; +using UnityEngine; namespace DCL.AvatarRendering.AvatarShape.Systems { - [UpdateInGroup(typeof(AvatarGroup))] - [UpdateAfter(typeof(StartAvatarMatricesCalculationSystem))] - [UpdateAfter(typeof(AvatarInstantiatorSystem))] + [UpdateInGroup(typeof(PreRenderingSystemGroup))] public partial class FinishAvatarMatricesCalculationSystem : BaseUnityLoopSystem { private readonly CustomSkinning skinningStrategy; + private readonly AvatarTransformMatrixJobWrapper jobWrapper; + private NativeArray currentResult; - internal FinishAvatarMatricesCalculationSystem(World world, CustomSkinning skinningStrategy) : base(world) + internal FinishAvatarMatricesCalculationSystem(World world, CustomSkinning skinningStrategy, + AvatarTransformMatrixJobWrapper jobWrapper) : base(world) { this.skinningStrategy = skinningStrategy; + this.jobWrapper = jobWrapper; } protected override void Update(float t) { + jobWrapper.CompleteBoneMatrixCalculations(); + currentResult = jobWrapper.job.BonesMatricesResult; ExecuteQuery(World); } @@ -31,7 +39,7 @@ protected override void Update(float t) private void Execute(ref AvatarTransformMatrixComponent avatarTransformMatrixComponent, ref AvatarCustomSkinningComponent computeShaderSkinning) { - skinningStrategy.ComputeSkinning(avatarTransformMatrixComponent.CompleteBoneMatrixCalculations(), ref computeShaderSkinning); + skinningStrategy.ComputeSkinning(currentResult, avatarTransformMatrixComponent.IndexInGlobalJobArray, ref computeShaderSkinning); } } } diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/StartAvatarMatricesCalculationSystem.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/StartAvatarMatricesCalculationSystem.cs index 1e4b957316..4496a5994d 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/StartAvatarMatricesCalculationSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/StartAvatarMatricesCalculationSystem.cs @@ -3,8 +3,10 @@ using Arch.SystemGroups; using DCL.AvatarRendering.AvatarShape.Components; using DCL.AvatarRendering.AvatarShape.UnityInterface; +using DCL.CharacterMotion.Components; using ECS.Abstract; using ECS.LifeCycle.Components; +using UnityEngine; namespace DCL.AvatarRendering.AvatarShape.Systems { @@ -16,18 +18,25 @@ namespace DCL.AvatarRendering.AvatarShape.Systems [UpdateAfter(typeof(AvatarInstantiatorSystem))] // right after AvatarBase is instantiated public partial class StartAvatarMatricesCalculationSystem : BaseUnityLoopSystem { - internal StartAvatarMatricesCalculationSystem(World world) : base(world) { } + private readonly AvatarTransformMatrixJobWrapper avatarTransformMatrixBatchJob; + + internal StartAvatarMatricesCalculationSystem(World world, AvatarTransformMatrixJobWrapper jobWrapper) : + base(world) + { + avatarTransformMatrixBatchJob = jobWrapper; + } protected override void Update(float t) { ExecuteQuery(World); + avatarTransformMatrixBatchJob.ScheduleBoneMatrixCalculation(); } [Query] [None(typeof(DeleteEntityIntention))] - private void Execute(ref AvatarBase avatarBase, ref AvatarTransformMatrixComponent transformMatrixComponent, ref AvatarShapeComponent avatarShapeComponent) + private void Execute(ref AvatarBase avatarBase, ref AvatarTransformMatrixComponent transformMatrixComponent) { - transformMatrixComponent.ScheduleBoneMatrixCalculation(avatarBase.transform.worldToLocalMatrix); + avatarTransformMatrixBatchJob.UpdateAvatar(avatarBase, ref transformMatrixComponent); } } } diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs deleted file mode 100644 index d048e98da2..0000000000 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Arch.Core; -using Arch.System; -using Arch.SystemGroups; -using DCL.AvatarRendering.AvatarShape.Components; -using DCL.Diagnostics; -using ECS.Abstract; -using ECS.Groups; -using ECS.LifeCycle.Components; -using System.Linq; - -namespace DCL.AvatarRendering.AvatarShape.Systems -{ - /// - /// Tracks potential bugs with AvatarMatrixCalculation - /// if didn't work out for some reason - /// - [UpdateInGroup(typeof(CleanUpGroup))] - [UpdateAfter(typeof(AvatarCleanUpSystem))] - public partial class TrackTransformMatrixSystem : BaseUnityLoopSystem - { - internal TrackTransformMatrixSystem(World world) : base(world) { } - - protected override void Update(float t) - { - TrackUnfinishedMatrixCalculationQuery(World); - TrackAbandonedTransformMatrixQuery(World); - } - - [Query] - private void TrackAbandonedTransformMatrix(Entity entity, ref AvatarTransformMatrixComponent avatarTransformMatrixComponent, in DeleteEntityIntention deleteEntityIntention) - { - if (!deleteEntityIntention.DeferDeletion && !avatarTransformMatrixComponent.disposed) - { - avatarTransformMatrixComponent.Dispose(); - ReportHub.LogError(ReportCategory.AVATAR, $"{nameof(AvatarTransformMatrixComponent)} was not disposed properly. Archetype:\n {LogArchetype(World.GetArchetype(entity))}"); - } - } - - [Query] - [None(typeof(DeleteEntityIntention))] - private void TrackUnfinishedMatrixCalculation(Entity entity, ref AvatarTransformMatrixComponent avatarTransformMatrixComponent) - { - if (!avatarTransformMatrixComponent.completed) - { - avatarTransformMatrixComponent.CompleteBoneMatrixCalculations(); - ReportHub.LogError(ReportCategory.AVATAR, $"{nameof(AvatarTransformMatrixComponent)} was not completed properly. {entity.ToString()} Archetype:\n {LogArchetype(World.GetArchetype(entity))}"); - } - } - - private static string LogArchetype(Archetype archetype) - { - return string.Join(",", archetype.Types.Select(p => p.Type.Name)); - } - } -} diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs.meta b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs.meta deleted file mode 100644 index 683c9d282f..0000000000 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Systems/TrackTransformMatrixSystem.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0238d62ade044273b2c2ba25a6f9a85d -timeCreated: 1714487032 \ No newline at end of file diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs new file mode 100644 index 0000000000..0accc2002e --- /dev/null +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs @@ -0,0 +1,108 @@ +using System.Collections; +using System.Collections.Generic; +using DCL.AvatarRendering.AvatarShape.Components; +using DCL.AvatarRendering.AvatarShape.ComputeShader; +using DCL.AvatarRendering.AvatarShape.UnityInterface; +using NUnit.Framework; +using UnityEngine; + +public class AvatarTransformMatrixJobWrapperShould : MonoBehaviour +{ + private AvatarTransformMatrixJobWrapper jobWrapper; + + [SetUp] + public void SetUp() + { + jobWrapper = new AvatarTransformMatrixJobWrapper(); + } + + [TearDown] + public void TearDown() + { + jobWrapper.Dispose(); + } + + + [Test] + public void AddNewAvatarIncrementsAvatarIndex() + { + var avatarBase = new GameObject().AddComponent(); + var transformMatrixComponent = new AvatarTransformMatrixComponent + { + IndexInGlobalJobArray = -1, bones = new Transform[ComputeShaderConstants.BONE_COUNT] + }; + + int initialIndex = transformMatrixComponent.IndexInGlobalJobArray; + + jobWrapper.UpdateAvatar(avatarBase, ref transformMatrixComponent); + + Assert.AreNotEqual(initialIndex, transformMatrixComponent.IndexInGlobalJobArray); + } + + [Test] + public void ResizeArraysDoublesCapacity() + { + // Add avatars until we exceed the initial capacity and force a resize + for (int i = 0; i < AvatarTransformMatrixJobWrapper.AVATAR_ARRAY_SIZE + 1; i++) + { + var avatarBase = new GameObject().AddComponent(); + var transformMatrixComponent = new AvatarTransformMatrixComponent + { + IndexInGlobalJobArray = -1, bones = new Transform[ComputeShaderConstants.BONE_COUNT] + }; + + jobWrapper.UpdateAvatar(avatarBase, ref transformMatrixComponent); + } + + // After resizing, the internal array size should be doubled + Assert.AreEqual(AvatarTransformMatrixJobWrapper.AVATAR_ARRAY_SIZE * 2, jobWrapper.currentAvatarAmountSupported); + } + + [Test] + public void ReleasedIndexesAreReused() + { + var avatarBase = new GameObject().AddComponent(); + var transformMatrixComponent1 = new AvatarTransformMatrixComponent + { + IndexInGlobalJobArray = -1, bones = new Transform[ComputeShaderConstants.BONE_COUNT] + }; + + var transformMatrixComponent2 = new AvatarTransformMatrixComponent + { + IndexInGlobalJobArray = -1, bones = new Transform[ComputeShaderConstants.BONE_COUNT] + }; + + // Add the first avatar + jobWrapper.UpdateAvatar(avatarBase, ref transformMatrixComponent1); + int firstIndex = transformMatrixComponent1.IndexInGlobalJobArray; + + // Release the first avatar + jobWrapper.ReleaseAvatar(ref transformMatrixComponent1); + + // Add a second avatar and check if it reuses the released index + jobWrapper.UpdateAvatar(avatarBase, ref transformMatrixComponent2); + int secondIndex = transformMatrixComponent2.IndexInGlobalJobArray; + + Assert.AreEqual(firstIndex, secondIndex); + } + + [Test] + public void MatrixAndBoolArraysResize() + { + // Fill the wrapper to trigger a resize + for (int i = 0; i < AvatarTransformMatrixJobWrapper.AVATAR_ARRAY_SIZE + 1; i++) + { + var avatarBase = new GameObject().AddComponent(); + var transformMatrixComponent = new AvatarTransformMatrixComponent + { + IndexInGlobalJobArray = -1, bones = new Transform[ComputeShaderConstants.BONE_COUNT] + }; + + jobWrapper.UpdateAvatar(avatarBase, ref transformMatrixComponent); + } + + // The matrices and bools should have been resized to double their initial size + Assert.AreEqual(AvatarTransformMatrixJobWrapper.AVATAR_ARRAY_SIZE * 2, jobWrapper.matrixFromAllAvatars.Length); + Assert.AreEqual(AvatarTransformMatrixJobWrapper.AVATAR_ARRAY_SIZE * 2, jobWrapper.updateAvatar.Length); + } +} \ No newline at end of file diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs.meta b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs.meta new file mode 100644 index 0000000000..7bdb99105c --- /dev/null +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/EditMode/AvatarTransformMatrixJobWrapperShould.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50fb233cd764a114b9044885b429ce5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/Instantiate/AvatarInstantiatorSystemShould.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/Instantiate/AvatarInstantiatorSystemShould.cs index a793d5a0f0..1e01a98ec7 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/Instantiate/AvatarInstantiatorSystemShould.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/Tests/Instantiate/AvatarInstantiatorSystemShould.cs @@ -121,7 +121,8 @@ private async Task SetupAsync() system = new AvatarInstantiatorSystem(world, budget, budget, avatarPoolRegistry, materialPoolHandler, computeShaderPool, Substitute.For(), new ComputeShaderSkinning(), new FixedComputeBufferHandler(10000, 4, 4), - new ObjectProxy(), defaultFaceFeaturesHandler, new WearableStorage()); + new ObjectProxy(), defaultFaceFeaturesHandler, new WearableStorage(), + new AvatarTransformMatrixJobWrapper()); } private IEmote GetMockEmote(string materialName, string category) diff --git a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/UnityInterface/AvatarBase.cs b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/UnityInterface/AvatarBase.cs index bb8ab9c493..606616d9ad 100644 --- a/Explorer/Assets/DCL/AvatarRendering/AvatarShape/UnityInterface/AvatarBase.cs +++ b/Explorer/Assets/DCL/AvatarRendering/AvatarShape/UnityInterface/AvatarBase.cs @@ -2,11 +2,14 @@ using System.Linq; using UnityEngine; using UnityEngine.Animations.Rigging; +using Random = UnityEngine.Random; namespace DCL.AvatarRendering.AvatarShape.UnityInterface { public class AvatarBase : MonoBehaviour, IAvatarView { + public int RandomID; + private List> animationOverrides; private AnimationClip lastEmote; @@ -93,6 +96,8 @@ private void Awake() if (!AvatarAnimator) return; + RandomID = Random.Range(0, 1000); + overrideController = new AnimatorOverrideController(AvatarAnimator.runtimeAnimatorController); animationOverrides = new List>(); overrideController.GetOverrides(animationOverrides); diff --git a/Explorer/Assets/DCL/AvatarRendering/DemoScripts/Systems/InstantiateRandomAvatarsSystem.cs b/Explorer/Assets/DCL/AvatarRendering/DemoScripts/Systems/InstantiateRandomAvatarsSystem.cs index 9409d1939f..4e1bdd99fc 100644 --- a/Explorer/Assets/DCL/AvatarRendering/DemoScripts/Systems/InstantiateRandomAvatarsSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/DemoScripts/Systems/InstantiateRandomAvatarsSystem.cs @@ -38,11 +38,13 @@ using System; using System.Collections.Generic; using System.Linq; +using ECS.Unity.Transforms.Components; using UnityEngine; using UnityEngine.Pool; using Utility; using Utility.PriorityQueue; using Avatar = DCL.Profiles.Avatar; +using Object = UnityEngine.Object; using ParamPromise = ECS.StreamableLoading.Common.AssetPromise; using Random = UnityEngine.Random; using RaycastHit = UnityEngine.RaycastHit; @@ -56,7 +58,8 @@ public partial class InstantiateRandomAvatarsSystem : BaseUnityLoopSystem { private const int MAX_AVATAR_NUMBER = 1000; - private static readonly QueryDescription AVATARS_QUERY = new QueryDescription().WithAll().WithNone(); + private static readonly QueryDescription AVATARS_QUERY = new QueryDescription() + .WithAll().WithNone(); private readonly IRealmData realmData; private readonly IComponentPool transformPool; @@ -75,6 +78,8 @@ public partial class InstantiateRandomAvatarsSystem : BaseUnityLoopSystem private int lastIndexInstantiated; private readonly AvatarRandomizerAsset avatarRandomizerAsset; + private bool networkAvatar; + internal InstantiateRandomAvatarsSystem( World world, IDebugContainerBuilder debugBuilder, @@ -86,9 +91,11 @@ AvatarRandomizerAsset avatarRandomizerAsset this.realmData = realmData; transformPool = componentPools; this.avatarRandomizerAsset = avatarRandomizerAsset; + networkAvatar = true; debugBuilder.TryAddWidget("Avatar Debug") ?.SetVisibilityBinding(debugVisibilityBinding = new DebugWidgetVisibilityBinding(false)) + .AddToggleField("Network avatar", evt => networkAvatar = evt.newValue, true) .AddIntFieldWithConfirmation(30, "Instantiate", AddRandomAvatar) .AddControl(new DebugConstLabelDef("Total Avatars"), new DebugLongMarkerDef(totalAvatarsInstantiated = new ElementBinding(0), DebugLongMarkerDef.Unit.NoFormat)) .AddSingleButton("Destroy All Avatars", DestroyAllAvatars) @@ -146,9 +153,11 @@ private void RandomizeWearablesOfAvatars() private void DestroyRandomAmountOfAvatars() { + World.Query(in AVATARS_QUERY, - entity => + (Entity entity, ref CharacterTransform transformComponent) => { + Object.Destroy(transformComponent.Transform.gameObject.GetComponent()); if (Random.Range(0, 3) == 0) { World.Add(entity, new DeleteEntityIntention()); @@ -159,8 +168,13 @@ private void DestroyRandomAmountOfAvatars() private void DestroyAllAvatars() { - // Input events are processed before Update - World.Add(in AVATARS_QUERY, new DeleteEntityIntention()); + World.Query(in AVATARS_QUERY, + (Entity entity, ref CharacterTransform transformComponent) => + { + Object.Destroy(transformComponent.Transform.gameObject.GetComponent()); + World.Add(entity, new DeleteEntityIntention()); + }); + totalAvatarsInstantiated.Value = 0; } @@ -289,41 +303,51 @@ private void CreateAvatar(ICharacterControllerSettings characterControllerSettin else { transformComp.Transform.position = new Vector3(startXPosition + (avatarIndex * 2), 3, startZPosition); } transformComp.Transform.name = $"RANDOM_AVATAR_{avatarIndex}"; - - CharacterController characterController = transformComp.Transform.TryGetComponent(out var component) - ? component - : transformComp.Transform.gameObject.AddComponent(); - characterController.radius = 0.4f; - characterController.height = 2; - characterController.center = Vector3.up; - characterController.slopeLimit = 50f; - characterController.gameObject.layer = PhysicsLayers.CHARACTER_LAYER; - + HashSet wearablesURN = new HashSet(); foreach (string wearable in wearables) wearablesURN.Add(new URN(wearable)); - var avatarShape = Profile.Create( + var profile = Profile.Create( StringUtils.GenerateRandomString(5), StringUtils.GenerateRandomString(5), new Avatar(BodyShape.FromStringSafe(bodyShape), wearablesURN, WearablesConstants.DefaultColors.GetRandomEyesColor(), WearablesConstants.DefaultColors.GetRandomHairColor(), WearablesConstants.DefaultColors.GetRandomSkinColor())); - World.Create(avatarShape, - transformComp, - characterController, - new CharacterRigidTransform(), - new CharacterAnimationComponent(), - new CharacterEmoteComponent(), - new CharacterPlatformComponent(), - new StunComponent(), - new FeetIKComponent(), - new HandsIKComponent(), - new HeadIKComponent(), - new JumpInputComponent(), - new MovementInputComponent(), - characterControllerSettings, - new RandomAvatar() - ); + + if (networkAvatar) + { + World.Create(profile, + transformComp, + new CharacterAnimationComponent(), + new CharacterEmoteComponent(), + new RandomAvatar()); + } + else + { + var characterController = transformComp.Transform.gameObject.AddComponent(); + characterController.radius = 0.4f; + characterController.height = 2; + characterController.center = Vector3.up; + characterController.slopeLimit = 50f; + characterController.gameObject.layer = PhysicsLayers.CHARACTER_LAYER; + + World.Create(profile, + transformComp, + characterController, + new CharacterRigidTransform(), + new CharacterAnimationComponent(), + new CharacterEmoteComponent(), + new CharacterPlatformComponent(), + new StunComponent(), + new FeetIKComponent(), + new HandsIKComponent(), + new HeadIKComponent(), + new JumpInputComponent(), + new MovementInputComponent(), + characterControllerSettings, + new RandomAvatar() + ); + } } private static Vector3 StartRandomPosition(float spawnArea, float startXPosition, float startZPosition) diff --git a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CalculateCharacterVelocitySystem.cs b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CalculateCharacterVelocitySystem.cs index 8f8eefbc85..d3fec66a69 100644 --- a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CalculateCharacterVelocitySystem.cs +++ b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CalculateCharacterVelocitySystem.cs @@ -11,6 +11,7 @@ using DCL.DebugUtilities.UIBindings; using DCL.Time.Systems; using ECS.Abstract; +using ECS.LifeCycle.Components; using UnityEngine; namespace DCL.CharacterMotion.Systems @@ -104,6 +105,7 @@ protected override void Update(float t) } [Query] + [None(typeof(DeleteEntityIntention))] private void ResolveVelocity( [Data] float dt, [Data] int physicsTick, diff --git a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CharacterPlatformSystem.cs b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CharacterPlatformSystem.cs index c2c9590320..6b7afa6f9b 100644 --- a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CharacterPlatformSystem.cs +++ b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/CharacterPlatformSystem.cs @@ -7,6 +7,7 @@ using DCL.CharacterMotion.Settings; using DCL.Diagnostics; using ECS.Abstract; +using ECS.LifeCycle.Components; using UnityEngine; namespace DCL.CharacterMotion.Systems @@ -26,7 +27,7 @@ protected override void Update(float _) } [Query] - [None(typeof(PlayerTeleportIntent))] + [None(typeof(PlayerTeleportIntent), typeof(DeleteEntityIntention))] private void ResolvePlatformMovement( in ICharacterControllerSettings settings, ref CharacterPlatformComponent platformComponent, diff --git a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/InterpolateCharacterSystem.cs b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/InterpolateCharacterSystem.cs index c6c422d6d2..e249aa80df 100644 --- a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/InterpolateCharacterSystem.cs +++ b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/InterpolateCharacterSystem.cs @@ -9,7 +9,9 @@ using DCL.CharacterMotion.Platforms; using DCL.CharacterMotion.Settings; using DCL.Diagnostics; +using Decentraland.SocialServiceV2; using ECS.Abstract; +using ECS.LifeCycle.Components; using UnityEngine; using Utility; @@ -53,7 +55,7 @@ private void TeleportPlayer(in Entity entity, in CharacterController controller, } [Query] - [None(typeof(PlayerTeleportIntent))] + [None(typeof(PlayerTeleportIntent), typeof(DeleteEntityIntention))] private void Interpolate( [Data] float dt, in ICharacterControllerSettings settings, diff --git a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/StunCharacterSystem.cs b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/StunCharacterSystem.cs index 9c40c279f5..f038ae0293 100644 --- a/Explorer/Assets/DCL/Character/CharacterMotion/Systems/StunCharacterSystem.cs +++ b/Explorer/Assets/DCL/Character/CharacterMotion/Systems/StunCharacterSystem.cs @@ -6,6 +6,7 @@ using DCL.CharacterMotion.Settings; using DCL.Diagnostics; using ECS.Abstract; +using ECS.LifeCycle.Components; using UnityEngine; namespace DCL.CharacterMotion.Systems @@ -30,6 +31,7 @@ protected override void Update(float t) } [Query] + [None(typeof(DeleteEntityIntention))] private void CheckStunStatus( [Data] float currentTime, ref CharacterRigidTransform rigidTransform, diff --git a/Explorer/Assets/DCL/PluginSystem/Global/AvatarPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/AvatarPlugin.cs index 2d52e344d3..42b05865f6 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/AvatarPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/AvatarPlugin.cs @@ -22,6 +22,7 @@ using System.Threading; using DCL.AvatarRendering; using DCL.AvatarRendering.AvatarShape; +using DCL.AvatarRendering.AvatarShape.Components; using DCL.AvatarRendering.AvatarShape.Helpers; using DCL.Multiplayer.Profiles.Entities; using DCL.AvatarRendering.Loading.Assets; @@ -31,7 +32,6 @@ using UnityEngine.Pool; using Utility; using Object = UnityEngine.Object; -using StartAvatarMatricesCalculationSystem = DCL.AvatarRendering.AvatarShape.Systems.StartAvatarMatricesCalculationSystem; namespace DCL.PluginSystem.Global { @@ -72,6 +72,8 @@ public class AvatarPlugin : IDCLGlobalPlugin private readonly ExposedTransform playerTransform; private readonly IWearableStorage wearableStorage; + private readonly AvatarTransformMatrixJobWrapper avatarTransformMatrixJobWrapper; + public AvatarPlugin( IComponentPoolsRegistry poolsRegistry, IAssetsProvisioner assetsProvisioner, @@ -105,6 +107,7 @@ ExposedTransform playerTransform this.playerTransform = playerTransform; this.wearableStorage = wearableStorage; componentPoolsRegistry = poolsRegistry; + avatarTransformMatrixJobWrapper = new AvatarTransformMatrixJobWrapper(); cacheCleaner.Register(attachmentsAssetsCache); } @@ -112,6 +115,7 @@ ExposedTransform playerTransform public void Dispose() { attachmentsAssetsCache.Dispose(); + avatarTransformMatrixJobWrapper.Dispose(); } public async UniTask InitializeAsync(AvatarShapeSettings settings, CancellationToken ct) @@ -143,18 +147,22 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder builder, foreach (var extendedObjectPool in avatarMaterialPoolHandler.GetAllMaterialsPools()) cacheCleaner.Register(extendedObjectPool.Pool); + AvatarInstantiatorSystem.InjectToWorld(ref builder, frameTimeCapBudget, memoryBudget, avatarPoolRegistry, avatarMaterialPoolHandler, computeShaderPool, attachmentsAssetsCache, skinningStrategy, vertOutBuffer, mainPlayerAvatarBaseProxy, defaultFaceFeaturesHandler, - wearableStorage); + wearableStorage, avatarTransformMatrixJobWrapper); MakeVertsOutBufferDefragmentationSystem.InjectToWorld(ref builder, vertOutBuffer, skinningStrategy); - StartAvatarMatricesCalculationSystem.InjectToWorld(ref builder); - FinishAvatarMatricesCalculationSystem.InjectToWorld(ref builder, skinningStrategy); + + StartAvatarMatricesCalculationSystem.InjectToWorld(ref builder, avatarTransformMatrixJobWrapper); + FinishAvatarMatricesCalculationSystem.InjectToWorld(ref builder, skinningStrategy, + avatarTransformMatrixJobWrapper); AvatarShapeVisibilitySystem.InjectToWorld(ref builder); - AvatarCleanUpSystem.InjectToWorld(ref builder, frameTimeCapBudget, vertOutBuffer, avatarMaterialPoolHandler, avatarPoolRegistry, computeShaderPool, attachmentsAssetsCache, mainPlayerAvatarBaseProxy); - TrackTransformMatrixSystem.InjectToWorld(ref builder); + AvatarCleanUpSystem.InjectToWorld(ref builder, frameTimeCapBudget, vertOutBuffer, avatarMaterialPoolHandler, + avatarPoolRegistry, computeShaderPool, attachmentsAssetsCache, mainPlayerAvatarBaseProxy, + avatarTransformMatrixJobWrapper); NametagPlacementSystem.InjectToWorld(ref builder, nametagViewPool, chatEntryConfiguration, nametagsData, chatBubbleConfiguration);