diff --git a/CHANGELOG.md b/CHANGELOG.md index 544a18bf56..e5c099a188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,15 @@ ## Unreleased +### Added + +- Added `MeansImplicitUse` attribute to `RequireAttribute` to reduce warnings in Rider IDE. [#1462](https://github.com/spatialos/gdk-for-unity/pull/1462) + ### Changed - Upgrade to Worker SDK v14.8.0. [#1458](https://github.com/spatialos/gdk-for-unity/pull/1458) - Migrated launch configurations to latest game templates. [#1457](https://github.com/spatialos/gdk-for-unity/pull/1457) -- Added `MeansImplicitUse` attribute to `RequireAttribute` to reduce warnings in Rider IDE. [#1462](https://github.com/spatialos/gdk-for-unity/pull/1462) +- Multithreaded component serialization through `SystemBase` jobs. [#1454](https://github.com/spatialos/gdk-for-unity/pull/1454) ## `0.3.10` - 2020-08-18 diff --git a/workers/unity/Assets/Playground/Config/EntityTemplates.cs b/workers/unity/Assets/Playground/Config/EntityTemplates.cs index f1c6233062..0ed5f18062 100644 --- a/workers/unity/Assets/Playground/Config/EntityTemplates.cs +++ b/workers/unity/Assets/Playground/Config/EntityTemplates.cs @@ -69,7 +69,7 @@ public static EntityTemplate CreateCubeEntityTemplate(Vector3 location) var template = new EntityTemplate(); template.AddComponent(new Position.Snapshot(location.ToCoordinates()), WorkerUtils.UnityGameLogic); template.AddComponent(new Metadata.Snapshot("Cube"), WorkerUtils.UnityGameLogic); - template.AddComponent(new Persistence.Snapshot(), WorkerUtils.UnityGameLogic); + template.AddComponent(new Persistence.Snapshot()); template.AddComponent(new CubeColor.Snapshot(), WorkerUtils.UnityGameLogic); template.AddComponent(new CubeTargetVelocity.Snapshot(new Vector3f(-2.0f, 0, 0)), WorkerUtils.UnityGameLogic); @@ -99,7 +99,7 @@ public static EntityTemplate CreateSpinnerEntityTemplate(Coordinates coords) template.AddComponent(new Position.Snapshot(coords), WorkerUtils.UnityGameLogic); template.AddComponent(new Metadata.Snapshot("Spinner"), WorkerUtils.UnityGameLogic); template.AddComponent(transform, WorkerUtils.UnityGameLogic); - template.AddComponent(new Persistence.Snapshot(), WorkerUtils.UnityGameLogic); + template.AddComponent(new Persistence.Snapshot()); template.AddComponent(new Collisions.Snapshot(), WorkerUtils.UnityGameLogic); template.AddComponent(new SpinnerColor.Snapshot(Color.BLUE), WorkerUtils.UnityGameLogic); template.AddComponent(new SpinnerRotation.Snapshot(), WorkerUtils.UnityGameLogic); @@ -121,9 +121,9 @@ public static EntityTemplate CreateSpinnerEntityTemplate(Coordinates coords) public static EntityTemplate CreatePlayerSpawnerEntityTemplate(Coordinates playerSpawnerLocation) { var template = new EntityTemplate(); - template.AddComponent(new Position.Snapshot(playerSpawnerLocation), WorkerUtils.UnityGameLogic); - template.AddComponent(new Metadata.Snapshot("PlayerCreator"), WorkerUtils.UnityGameLogic); - template.AddComponent(new Persistence.Snapshot(), WorkerUtils.UnityGameLogic); + template.AddComponent(new Position.Snapshot(playerSpawnerLocation)); + template.AddComponent(new Metadata.Snapshot("PlayerCreator")); + template.AddComponent(new Persistence.Snapshot()); template.AddComponent(new PlayerCreator.Snapshot(), WorkerUtils.UnityGameLogic); return template; diff --git a/workers/unity/Assets/Tests/PlaymodeTests/Performance/ComponentUpdateTests.cs b/workers/unity/Assets/Tests/PlaymodeTests/Performance/ComponentUpdateTests.cs index 14a688b0df..4b80938e51 100644 --- a/workers/unity/Assets/Tests/PlaymodeTests/Performance/ComponentUpdateTests.cs +++ b/workers/unity/Assets/Tests/PlaymodeTests/Performance/ComponentUpdateTests.cs @@ -26,11 +26,10 @@ public void SinglePositionUpdate() { var markers = new[] { - "GatherAllChunks", - "ExecuteReplication", - "Position.SendUpdates", + "SpatialOSSendSystem.CompletingAllJobs", + "SpatialOSSendSystem.QueueSerializedMessages", "WorkerSystem.SendMessages", - "Position.ComponentSerializer.Serialize" + "PositionSerializationJob" }; var currentState = World.Step(world => diff --git a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/CoreCodegenJob.cs b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/CoreCodegenJob.cs index cda8a3a785..1648580860 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/CoreCodegenJob.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/CoreCodegenJob.cs @@ -52,7 +52,7 @@ public CoreCodegenJob(CodegenJobOptions options, IFileSystem fileSystem, Details Logger.Trace("Adding job targets for components."); AddGenerators(componentsToGenerate, c => ($"{c.Name}.cs", UnityComponentDataGenerator.Generate), - c => ($"{c.Name}UpdateSender.cs", UnityComponentSenderGenerator.Generate), + c => ($"{c.Name}ReplicationSystem.cs", UnityComponentReplicationSystemGenerator.Generate), c => ($"{c.Name}EcsViewManager.cs", UnityEcsViewManagerGenerator.Generate), c => ($"{c.Name}ComponentDiffStorage.cs", ComponentDiffStorageGenerator.Generate), c => ($"{c.Name}ComponentDiffDeserializer.cs", ComponentDiffDeserializerGenerator.Generate), diff --git a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/ComponentDiffDeserializerGenerator.cs b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/ComponentDiffDeserializerGenerator.cs index 3a1b728c0d..8b917bcab8 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/ComponentDiffDeserializerGenerator.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/ComponentDiffDeserializerGenerator.cs @@ -109,20 +109,7 @@ public uint GetComponentId() { m.ProfileScope("serializeMarker", s => { - s.Line(@" -var storage = messages.GetComponentDiffStorage(ComponentId); - -var updates = ((IDiffUpdateStorage) storage).GetUpdates(); - -for (int i = 0; i < updates.Count; ++i) -{ - ref readonly var update = ref updates[i]; - var schemaUpdate = SchemaComponentUpdate.Create(); - var componentUpdate = new ComponentUpdate(ComponentId, schemaUpdate); - Serialization.SerializeUpdate(update.Update, schemaUpdate); - serializedMessages.AddComponentUpdate(componentUpdate, update.EntityId.Id); -} -"); + s.Line("var storage = messages.GetComponentDiffStorage(ComponentId);"); foreach (var ev in eventDetailsList) { @@ -138,7 +125,7 @@ public uint GetComponentId() var componentUpdate = new ComponentUpdate(ComponentId, schemaUpdate); var obj = schemaUpdate.GetEvents().AddObject({ev.EventIndex}); {ev.FqnPayloadType}.Serialization.Serialize(ev.Event.Payload, obj); - serializedMessages.AddComponentUpdate(componentUpdate, ev.EntityId.Id); + serializedMessages.AddComponentEvent(componentUpdate, ev.EntityId.Id); }} " }); diff --git a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/MetaclassGenerator.cs b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/MetaclassGenerator.cs index fe904f02eb..4bca197c24 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/MetaclassGenerator.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/MetaclassGenerator.cs @@ -40,7 +40,7 @@ public static CodeWriter Generate(UnityComponentDetails componentDetails) public Type Snapshot {{ get; }} = typeof({rootNamespace}.Snapshot); public Type Update {{ get; }} = typeof({rootNamespace}.Update); -public Type ReplicationHandler {{ get; }} = typeof({rootNamespace}.ComponentReplicator); +public Type ReplicationSystem {{ get; }} = typeof({rootNamespace}.ReplicationSystem); public Type Serializer {{ get; }} = typeof({rootNamespace}.ComponentSerializer); public Type DiffDeserializer {{ get; }} = typeof({rootNamespace}.DiffComponentDeserializer); diff --git a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentReplicationSystemGenerator.cs b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentReplicationSystemGenerator.cs new file mode 100644 index 0000000000..32858d98d5 --- /dev/null +++ b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentReplicationSystemGenerator.cs @@ -0,0 +1,98 @@ +using Improbable.Gdk.CodeGeneration.CodeWriter; +using Improbable.Gdk.CodeGeneration.Model.Details; +using NLog; + +namespace Improbable.Gdk.CodeGenerator +{ + public static class UnityComponentReplicationSystemGenerator + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public static CodeWriter Generate(UnityComponentDetails componentDetails) + { + var componentNamespace = $"global::{componentDetails.Namespace}.{componentDetails.Name}"; + + Logger.Trace($"Generating {componentDetails.Namespace}.{componentDetails.Name}.ReplicationSystem internal class."); + + return CodeWriter.Populate(cgw => + { + cgw.UsingDirectives( + "Improbable.Gdk.Core", + "Improbable.Worker.CInterop", + "Unity.Collections", + "Unity.Entities", + "Unity.Profiling" + ); + + cgw.Namespace(componentDetails.Namespace, ns => + { + ns.Type($"public partial class {componentDetails.Name}", partial => + { + partial.Annotate("DisableAutoCreation, UpdateInGroup(typeof(SpatialOSSendGroup)), UpdateBefore(typeof(SpatialOSSendGroup.InternalSpatialOSSendGroup))") + .Type("internal class ReplicationSystem : SystemBase", system => + { + system.Line($@" +private NativeQueue dirtyComponents; +private SpatialOSSendSystem spatialOsSendSystem; + +private ProfilerMarker foreachMarker = new ProfilerMarker(""{componentDetails.Name}SerializationJob""); + +protected override void OnCreate() +{{ + spatialOsSendSystem = World.GetExistingSystem(); +}} +"); + system.Method("protected override void OnUpdate()", m => + { + m.Line(new[] + { + "dirtyComponents = new NativeQueue(Allocator.TempJob);", + "var dirtyComponentsWriter = dirtyComponents.AsParallelWriter();", + "var marker = foreachMarker;", + }); + + m.Line($@" +Dependency = Entities.WithName(""{componentDetails.Name}Replication"")"); + if (!componentDetails.IsBlittable) + { + m.Line(@" + .WithoutBurst()"); + } + + m.Line(@" + .WithAll() + .WithChangeFilter() + .ForEach((ref Component component, in SpatialEntityId entity) => + { + marker.Begin(); + if (!component.IsDataDirty()) + { + marker.End(); + return; + } + + // Serialize component + var schemaUpdate = SchemaComponentUpdate.Create(); + Serialization.SerializeUpdate(component, schemaUpdate); + + component.MarkDataClean(); + + // Schedule update + var componentUpdate = new ComponentUpdate(ComponentId, schemaUpdate); + var update = new SerializedMessagesToSend.UpdateToSend(componentUpdate, entity.EntityId.Id); + dirtyComponentsWriter.Enqueue(update); + marker.End(); + }) + .ScheduleParallel(Dependency); + +spatialOsSendSystem.AddReplicationJobProducer(Dependency, dirtyComponents); +dirtyComponents = default; +"); + }); + }); + }); + }); + }); + } + } +} diff --git a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentSenderGenerator.cs b/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentSenderGenerator.cs deleted file mode 100644 index 6713e73a89..0000000000 --- a/workers/unity/Packages/io.improbable.gdk.core/.codegen/Source/Generators/Core/UnityComponentSenderGenerator.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Improbable.Gdk.CodeGeneration.CodeWriter; -using Improbable.Gdk.CodeGeneration.Model.Details; -using NLog; - -namespace Improbable.Gdk.CodeGenerator -{ - public static class UnityComponentSenderGenerator - { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public static CodeWriter Generate(UnityComponentDetails componentDetails) - { - var componentNamespace = $"global::{componentDetails.Namespace}.{componentDetails.Name}"; - - Logger.Trace($"Generating {componentDetails.Namespace}.{componentDetails.Name}.ComponentReplicator internal class."); - - return CodeWriter.Populate(cgw => - { - cgw.UsingDirectives( - "Unity.Entities", - "Unity.Collections", - "Improbable.Gdk.Core", - "Improbable.Gdk.Core.CodegenAdapters", - "Unity.Profiling" - ); - - cgw.Namespace(componentDetails.Namespace, ns => - { - ns.Type($"public partial class {componentDetails.Name}", partial => - { - partial.Type("internal class ComponentReplicator : IComponentReplicationHandler", replicator => - { - replicator.Line($@" -private ProfilerMarker componentMarker = new ProfilerMarker(""{componentDetails.Name}.SendUpdates""); - -public uint ComponentId => {componentDetails.ComponentId}; - -public EntityQueryDesc ComponentUpdateQuery => new EntityQueryDesc -{{ - All = new[] - {{ - ComponentType.ReadWrite<{componentNamespace}.Component>(), - ComponentType.ReadOnly<{componentNamespace}.HasAuthority>(), - ComponentType.ReadOnly() - }}, -}}; -"); - replicator.Method(@" -public void SendUpdates( - NativeArray chunkArray, - ComponentSystemBase system, - EntityManager entityManager, - ComponentUpdateSystem componentUpdateSystem) -", m => - { - m.ProfileScope("componentMarker", s => - { - s.Line(new[] - { - "var spatialOSEntityType = system.GetArchetypeChunkComponentType(true);", - $"var componentType = system.GetArchetypeChunkComponentType<{componentNamespace}.Component>();", - }); - - s.Loop("foreach (var chunk in chunkArray)", outerLoop => - { - outerLoop.Line(new[] - { - "var entityIdArray = chunk.GetNativeArray(spatialOSEntityType);", - "var componentArray = chunk.GetNativeArray(componentType);", - }); - - outerLoop.Loop("for (var i = 0; i < componentArray.Length; i++)", innerLoop => - { - innerLoop.Line("var data = componentArray[i];"); - - innerLoop.If("data.IsDataDirty()", componentDirtyThen => - { - componentDirtyThen.Line("var update = new Update();"); - for (var i = 0; i < componentDetails.FieldDetails.Count; i++) - { - var fieldDetails = componentDetails.FieldDetails[i]; - componentDirtyThen.If($"data.IsDataDirty({i})", () => new[] - { - $"update.{fieldDetails.PascalCaseName} = data.{fieldDetails.PascalCaseName};" - }); - } - - componentDirtyThen.Line(new[] - { - "componentUpdateSystem.SendUpdate(in update, entityIdArray[i].EntityId);", - "data.MarkDataClean();", - "componentArray[i] = data;" - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); - } - } -} diff --git a/workers/unity/Packages/io.improbable.gdk.core/Components/IComponentMetaclass.cs b/workers/unity/Packages/io.improbable.gdk.core/Components/IComponentMetaclass.cs index 7278501198..0432ad86b5 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Components/IComponentMetaclass.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Components/IComponentMetaclass.cs @@ -13,7 +13,7 @@ public interface IComponentMetaclass Type Snapshot { get; } Type Update { get; } - Type ReplicationHandler { get; } + Type ReplicationSystem { get; } Type Serializer { get; } Type DiffDeserializer { get; } diff --git a/workers/unity/Packages/io.improbable.gdk.core/Improbable.Gdk.Core.asmdef b/workers/unity/Packages/io.improbable.gdk.core/Improbable.Gdk.Core.asmdef index 2b81a4319b..eab7afeef3 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Improbable.Gdk.Core.asmdef +++ b/workers/unity/Packages/io.improbable.gdk.core/Improbable.Gdk.Core.asmdef @@ -3,7 +3,8 @@ "references": [ "Unity.Entities", "Unity.Entities.Hybrid", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.Collections" ], "includePlatforms": [], "excludePlatforms": [], @@ -12,5 +13,6 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [] + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs b/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs deleted file mode 100644 index 053f758cf9..0000000000 --- a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Improbable.Gdk.Core.CodegenAdapters; -using Unity.Collections; -using Unity.Entities; -using Unity.Jobs; -using Unity.Jobs.LowLevel.Unsafe; -using Unity.Profiling; -using UnityEngine.Profiling; - -namespace Improbable.Gdk.Core -{ - /// - /// Executes the default replication logic for each SpatialOS component. - /// - [DisableAutoCreation] - [AlwaysUpdateSystem] - [UpdateInGroup(typeof(SpatialOSSendGroup.InternalSpatialOSSendGroup))] - [UpdateBefore(typeof(SpatialOSSendSystem))] - public class ComponentSendSystem : ComponentSystem - { - private readonly List componentReplicators = new List(); - private NativeArray[] chunkArrayCache; - private NativeArray gatheringJobs; - - private ProfilerMarker gatherChunksMarker = new ProfilerMarker("GatherAllChunks"); - private ProfilerMarker executeReplication = new ProfilerMarker("ExecuteReplication"); - - protected override void OnCreate() - { - base.OnCreate(); - - PopulateDefaultComponentReplicators(); - chunkArrayCache = new NativeArray[componentReplicators.Count]; - gatheringJobs = new NativeArray(componentReplicators.Count, Allocator.Persistent); - } - - protected override void OnDestroy() - { - gatheringJobs.Dispose(); - base.OnDestroy(); - } - - protected override void OnUpdate() - { - var componentUpdateSystem = World.GetExistingSystem(); - - using (gatherChunksMarker.Auto()) - { - for (var i = 0; i < componentReplicators.Count; i++) - { - var replicator = componentReplicators[i]; - if (replicator.Group.IsEmptyIgnoreFilter) - { - continue; - } - - chunkArrayCache[i] = - replicator.Group.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var jobHandle); - gatheringJobs[i] = jobHandle; - } - } - - for (var i = 0; i < componentReplicators.Count; i++) - { - using (executeReplication.Auto()) - { - var replicator = componentReplicators[i]; - var chunkArray = chunkArrayCache[i]; - - if (!chunkArray.IsCreated) - { - continue; - } - - // Wait for gathering to complete - gatheringJobs[i].Complete(); - - // Process - replicator.Handler.SendUpdates(chunkArray, this, EntityManager, componentUpdateSystem); - - // Cleanup - chunkArray.Dispose(); - chunkArrayCache[i] = default; - } - } - } - - internal void AddComponentReplicator(IComponentReplicationHandler componentReplicationHandler) - { - componentReplicators.Add(new ComponentReplicator - { - ComponentId = componentReplicationHandler.ComponentId, - Handler = componentReplicationHandler, - Group = GetEntityQuery(componentReplicationHandler.ComponentUpdateQuery) - }); - } - - private void PopulateDefaultComponentReplicators() - { - // Find all component specific replicators and create an instance. - var types = ComponentDatabase.Metaclasses.Select(type => type.Value.ReplicationHandler); - foreach (var type in types) - { - if (type.GetCustomAttribute() != null) - { - continue; - } - - var handler = (IComponentReplicationHandler) Activator.CreateInstance(type); - - AddComponentReplicator(handler); - } - } - - private struct ComponentReplicator - { - public uint ComponentId; - public IComponentReplicationHandler Handler; - public EntityQuery Group; - } - } -} diff --git a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs.meta b/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs.meta deleted file mode 100644 index 0124385bcb..0000000000 --- a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentSendSystem.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a4c7894811316b240a6c734fe0e98f18 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentUpdateSystem.cs b/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentUpdateSystem.cs index 143f0fef09..94fb034078 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentUpdateSystem.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Systems/ComponentUpdateSystem.cs @@ -9,11 +9,6 @@ public class ComponentUpdateSystem : ComponentSystem { private WorkerSystem worker; - public void SendUpdate(in T update, EntityId entityId) where T : struct, ISpatialComponentUpdate - { - worker.MessagesToSend.AddComponentUpdate(in update, entityId.Id); - } - public void SendEvent(T eventToSend, EntityId entityId) where T : IEvent { worker.MessagesToSend.AddEvent(eventToSend, entityId.Id); diff --git a/workers/unity/Packages/io.improbable.gdk.core/Systems/SpatialOSSendSystem.cs b/workers/unity/Packages/io.improbable.gdk.core/Systems/SpatialOSSendSystem.cs index c533bbfbb7..d207a7a0bd 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Systems/SpatialOSSendSystem.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Systems/SpatialOSSendSystem.cs @@ -1,5 +1,9 @@ +using System.Collections.Generic; using Improbable.Gdk.Core.NetworkStats; +using Unity.Collections; using Unity.Entities; +using Unity.Jobs; +using Unity.Profiling; namespace Improbable.Gdk.Core { @@ -12,19 +16,62 @@ public class SpatialOSSendSystem : ComponentSystem private NetworkStatisticsSystem networkStatisticsSystem; private NetFrameStats netFrameStats = new NetFrameStats(); + private NativeList replicationHandles; + private List> componentQueues; + + private ProfilerMarker completingJobsMarker = new ProfilerMarker("SpatialOSSendSystem.CompletingAllJobs"); + private ProfilerMarker updateQueueMarker = new ProfilerMarker("SpatialOSSendSystem.QueueSerializedMessages"); + protected override void OnCreate() { base.OnCreate(); worker = World.GetExistingSystem(); networkStatisticsSystem = World.GetOrCreateSystem(); + replicationHandles = new NativeList(Allocator.Persistent); + componentQueues = new List>(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + replicationHandles.Dispose(); } protected override void OnUpdate() { + using (completingJobsMarker.Auto()) + { + JobHandle.CompleteAll(replicationHandles); + } + + replicationHandles.Clear(); + + using (updateQueueMarker.Auto()) + { + foreach (var componentQueue in componentQueues) + { + while (componentQueue.TryDequeue(out var updateToSend)) + { + worker.MessagesToSend.AddSerializedComponentUpdate(updateToSend); + } + + componentQueue.Dispose(); + } + } + worker.SendMessages(netFrameStats); + networkStatisticsSystem.AddOutgoingSample(netFrameStats); netFrameStats.Clear(); + + componentQueues.Clear(); + } + + public void AddReplicationJobProducer(JobHandle job, NativeQueue componentQueue) + { + replicationHandles.Add(job); + componentQueues.Add(componentQueue); } } } diff --git a/workers/unity/Packages/io.improbable.gdk.core/Worker/ConnectionHandlers/SpatialOSConnectionHandler/SpatialOSConnectionHandler.cs b/workers/unity/Packages/io.improbable.gdk.core/Worker/ConnectionHandlers/SpatialOSConnectionHandler/SpatialOSConnectionHandler.cs index 178e181eae..03d5ec70ef 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Worker/ConnectionHandlers/SpatialOSConnectionHandler/SpatialOSConnectionHandler.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Worker/ConnectionHandlers/SpatialOSConnectionHandler/SpatialOSConnectionHandler.cs @@ -2,6 +2,7 @@ using Improbable.Gdk.Core.Commands; using Improbable.Gdk.Core.NetworkStats; using Improbable.Worker.CInterop; +using Unity.Profiling; namespace Improbable.Gdk.Core { @@ -15,6 +16,10 @@ public class SpatialOSConnectionHandler : IConnectionHandler private readonly Connection connection; + private ProfilerMarker serializeFromMarker = new ProfilerMarker("SpatialOSConnectionHandler.SerializeFrom"); + private ProfilerMarker sendClearMarker = new ProfilerMarker("SpatialOSConnectionHandler.SendAndClear"); + private ProfilerMarker clearMarker = new ProfilerMarker("SpatialOSConnectionHandler.Clear"); + public SpatialOSConnectionHandler(Connection connection) { this.connection = connection; @@ -57,11 +62,20 @@ public MessagesToSend GetMessagesToSendContainer() public void PushMessagesToSend(MessagesToSend messages, NetFrameStats frameStats) { - serializedMessagesToSend.SerializeFrom(messages, commandMetaData); - serializedMessagesToSend.SendAndClear(connection, commandMetaData, frameStats); + using (serializeFromMarker.Auto()) + { + serializedMessagesToSend.SerializeFrom(messages, commandMetaData); + } + + using (sendClearMarker.Auto()) + { + serializedMessagesToSend.SendAndClear(connection, commandMetaData, frameStats); + } - serializedMessagesToSend.Clear(); - messages.Clear(); + using (clearMarker.Auto()) + { + messages.Clear(); + } } public void Dispose() diff --git a/workers/unity/Packages/io.improbable.gdk.core/Worker/MessagesToSend.cs b/workers/unity/Packages/io.improbable.gdk.core/Worker/MessagesToSend.cs index eaff6db806..9673e36f64 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Worker/MessagesToSend.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Worker/MessagesToSend.cs @@ -17,6 +17,8 @@ private static class MessagesToSendMetadata .Select(componentCommands => (componentCommands.Key, componentCommands.Value.Commands.Select(m => m.SendStorage))); } + private readonly MessageList serializedComponentUpdates; + private readonly Dictionary componentIdToComponentStorage = new Dictionary(); @@ -41,6 +43,8 @@ private static class MessagesToSendMetadata public MessagesToSend() { + serializedComponentUpdates = new MessageList(); + foreach (var (componentId, diffStorageType) in MessagesToSendMetadata.ComponentTypes) { var instance = (IComponentDiffStorage) Activator.CreateInstance(diffStorageType); @@ -89,16 +93,12 @@ public void Clear() logsToSend.Clear(); metricsToSend.Clear(); + serializedComponentUpdates.Clear(); } - public void AddComponentUpdate(in T update, long entityId) - where T : ISpatialComponentUpdate + public void AddSerializedComponentUpdate(in SerializedMessagesToSend.UpdateToSend serializedComponentUpdate) { - var storage = GetComponentDiffStorage(typeof(T)); - - // Update ID isn't needed so we set it to 0 - ((IDiffUpdateStorage) storage).AddUpdate(new ComponentUpdateReceived(update, new EntityId(entityId), - 0)); + serializedComponentUpdates.Add(serializedComponentUpdate); } public void AddEvent(T ev, long entityId) where T : IEvent @@ -133,6 +133,11 @@ public void AddMetrics(Metrics metrics) metricsToSend.Add(metrics); } + internal MessageList GetSerializedComponentUpdates() + { + return serializedComponentUpdates; + } + internal MessageList GetLogMessages() { return logsToSend; diff --git a/workers/unity/Packages/io.improbable.gdk.core/Worker/SerializedMessagesToSend.cs b/workers/unity/Packages/io.improbable.gdk.core/Worker/SerializedMessagesToSend.cs index 7e46339c9a..bec1cd1b86 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Worker/SerializedMessagesToSend.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Worker/SerializedMessagesToSend.cs @@ -91,6 +91,8 @@ public SerializedMessagesToSend() public void SerializeFrom(MessagesToSend messages, CommandMetaData commandMetaData) { + messages.GetSerializedComponentUpdates().CopyTo(updates); + foreach (var serializer in commandSerializers) { serializer.Serialize(messages, this, commandMetaData); @@ -130,6 +132,7 @@ public void SendAndClear(Connection connection, CommandMetaData commandMetaData, { ref readonly var update = ref updates[i]; connection.SendComponentUpdate(update.EntityId, update.Update, UpdateParams); + netFrameStats.AddUpdate(update.Update); } for (var i = 0; i < requests.Count; ++i) @@ -201,10 +204,9 @@ public void SendAndClear(Connection connection, CommandMetaData commandMetaData, Clear(); } - public void AddComponentUpdate(ComponentUpdate update, long entityId) + public void AddComponentEvent(ComponentUpdate update, long entityId) { updates.Add(new UpdateToSend(update, entityId)); - netFrameStats.AddUpdate(update); } public void AddRequest(CommandRequest request, uint commandId, long entityId, uint? timeout, CommandRequestId requestId) @@ -272,7 +274,7 @@ internal void DestroyUnsentMessages() #region Containers - private readonly struct UpdateToSend + public readonly struct UpdateToSend { public readonly ComponentUpdate Update; public readonly long EntityId; diff --git a/workers/unity/Packages/io.improbable.gdk.core/Worker/WorkerInWorld.cs b/workers/unity/Packages/io.improbable.gdk.core/Worker/WorkerInWorld.cs index 9f04343c7e..8275cc17f6 100644 --- a/workers/unity/Packages/io.improbable.gdk.core/Worker/WorkerInWorld.cs +++ b/workers/unity/Packages/io.improbable.gdk.core/Worker/WorkerInWorld.cs @@ -71,7 +71,6 @@ private void AddCoreSystems() World.GetOrCreateSystem(); World.GetOrCreateSystem(); World.GetOrCreateSystem(); - World.GetOrCreateSystem(); World.GetOrCreateSystem(); World.GetOrCreateSystem(); World.GetOrCreateSystem(); @@ -84,6 +83,11 @@ private void AddCoreSystems() World.GetOrCreateSystem(); World.GetOrCreateSystem(); World.GetOrCreateSystem(); + + foreach (var component in ComponentDatabase.Metaclasses.Values) + { + World.CreateSystem(component.ReplicationSystem); + } } public override void Dispose() diff --git a/workers/unity/Packages/manifest.json b/workers/unity/Packages/manifest.json index a2b3593eed..3fbbd6ac95 100644 --- a/workers/unity/Packages/manifest.json +++ b/workers/unity/Packages/manifest.json @@ -1,6 +1,7 @@ { "dependencies": { - "com.unity.ide.rider": "1.1.4", + "com.unity.ide.rider": "1.2.1", + "com.unity.ide.visualstudio": "2.0.1", "com.unity.quicksearch": "2.0.0", "com.unity.testtools.codecoverage": "0.2.3-preview", "com.unity.ugui": "1.0.0",