From f0bcb0dd1b1b27418b841801c27713372ad1aefa Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Tue, 4 Aug 2020 00:00:00 +0000 Subject: [PATCH] com.unity.entities@0.14.0-preview.18 ## [0.14.0] - 2020-08-04 ### Added * Added `IsEmpty` property to `DynamicBuffer`. * Added deduplication for asset bundles generated for subscenes. * Added new `EntityManager` methods: `AddComponent(EntityQuery, ComponentTypes)`, which adds multiple components to all entities matching a query; and `RemoveComponent(Entity, ComponentTypes)`, which removes multiple components from a single entity. (`AddComponent(Entity, ComponentTypes)` and `RemoveComponent(EntityQuery, ComponentTypes)` already existed. This patch just fills in a few 'missing' methods.) ### Changed * `IJobEntityBatch.ScheduleSingle` is being renamed to `IJobEntityBatch.Schedule` to match our naming guidelines for job scheduling. * When `DefaultWorldInitialization.Initialize()` adds the default World's system groups to the Unity player loop, it now bases its modifications on the current player loop instead of the default player loop. This prevents the Entities package from accidentally erasing any previous player loop modifications made outside the package. * `DefaultWorldInitialization.DomainUnloadOrPlayModeChangeShutdown()` now removes all existing `World`s from the Unity player loop before destroying them. If a `World` that was added to the player loop is destroyed manually prior to domain unload, it must also be removed from the player loop manually using `ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop()`. * Updated package `com.unity.platforms` to version `0.7.0-preview.8`. * `EntityManager.CreateEntity()`, `EntityManager.SetArchetype()`, and `EntityCommandBuffer.CreateEntity()` no longer accept the value returned by `new EntityArchetype()` because it's invalid. Same for `EntityCommandBuffer.CreateEntity()` and `EntityCommandBuffer.ParallelWriter.CreateEntity()`. Always use `EntityManager.CreateArchetype()` instead of `new EntityArchetype()` to create `EntityArchetype` values. (Ideally, the `EntityArchetype` constructor wouldn't be public, but C# doesn't allow that for a struct.) * Subscene Inspector now uses a table format to allow easier management of multiple subscenes ### Deprecated * `IJobEntityBatch.ScheduleParallelBatched` is being deprecated in favor of adding a batching parameter to `IJobEntityBatch.ScheduleParallel` * `ScriptBehaviourUpdateOrder.UpdatePlayerLoop()` is being deprecated in favor of `ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop()`. Due to slightly different semantics, a direct automated API update is not possible: the new function always takes a `PlayerLoopSystem` object to modify, does not call `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()`, and does not create the top-level system groups if they don't exist. * `ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(World)` is being deprecated in favor of `ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(World)`. ### Removed * Removed obsolete `ScriptBehaviourUpdateOrder.CurrentPlayerLoop`. Use `UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop()` instead. * Removed obsolete `ScriptBehaviourUpdateOrder.SetPlayerLoop()`. Use `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()` instead. ### Fixed * Setting the Scene Asset on a Subscene would sometimes fail to trigger an import/conversion because the default ECS world was missing. * Fixed crash when using Singleton access methods (GetSingleton, SetSingleton, etc.) with a generic parameter as argument. * Fixed an issue which caused WebGL not to work, and could produce this error message on IL2CPP-based backends: ``` NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: Unity.Entities.SystemBase::UnmanagedUpdate ## [0.13.0] - 2020-07-10 ### Added * Added new `EntityCommandBuffer` methods: `AddComponent(Entity, ComponentTypes)` and `AddComponent(EntityQuery, ComponentTypes)` for adding multiple components in one call. (`EntityManager` has an equivalent of the first already and will get an equivalent of the second later.) * Added new `EntityCommandBuffer` methods: `RemoveComponent(Entity, ComponentTypes)` and `RemoveComponent(EntityQuery, ComponentTypes)` for removing multiple components in one call. (`EntityManager` will get equivalents in the future.) * Added new `IJobEntityBatchWithIndex` job interface, a variant of `IJobEntityBatch` that provides an additional `indexOfFirstEntityInQuery` parameter, which provides a per-batch index that is the aggregate of all previous batch counts. * Added `MaximumDeltaTime` property to `FixedStepSimulationSystemGroup`, used similarly to `UnityEngine.Time.maximumDeltaTime` to control how gradually the application should recover from large transient frame time spikes. * Added new player loop management functions to the `ScriptBehaviourUpdateOrder` class: * `AppendSystemToPlayerLoopList()`: adds a single ECS system to a specific point in the Unity player loop. * `AddWorldToPlayerLoop()`: adds the three standard top-level system groups to their standard player loop locations. * `IsWorldInPlayerLoop(World, PlayerLoopSystem)`: searches the provided player loop for any systems owned by the provided World. * `RemoveWorldFromPlayerLoop()`: removes all systems owned by a World from the provided player loop. * `AddWorldToCurrentPlayerLoop()`, `IsWorldInCurrentPlayerLoop()`, and `RemoveWorldFromCurrentPlayerLoop()`: wrappers around the above functions that operate directly on the currently active player loop. ### Changed * Updated minimum Unity Editor version to 2020.1.0b15 (40d9420e7de8) * Profiler markers for `EntityCommandBuffer.Playback` from `EntityCommandBufferSystem`s now include name of the system that recorded the `EntityCommandBuffer`. * Bumped burst to 1.3.2 version. * EntityQuery commands for `AddComponent`, `RemoveComponent`, and `DestroyEntity` in the EntityCommandBuffer now use Burst during Playback. * IJobChunk and Entities.ForEach ScheduleParallel has been optimized in case there is no EntityQuery filtering necessary (Shared component or change filtering) * `TypeManager.GetSystems()` now returns an `IReadOnlyList` rather than a `List` * Updated package `com.unity.platforms` to version `0.6.0-preview.4`. * `EntityContainer` will now allow to write data back to the entity. * Updated package `com.unity.properties` and `com.unity.serialization` to version `1.3.1-preview`. ### Fixed * Fixed warning treated as error in the case that a warning is emitted for Entities.ForEach passing a component type as value. * Fixed paths displayed in IL post-processing error messages to be more consistent with Unity error messages. * Fixed exceptions being thrown when inspecting an entity with a GameObject added through `EntityManager.AddComponentObject`. * Fixed `DCICE002` error thrown during IL post-processing when `Entities.ForEach` contains multiple Entities.ForEach in same scope capturing multiple variables. * `EntityManager`'s `AddComponent()`, `RemoveComponent()`, and `CopyEntitiesFrom()` methods no longer throw an error if their input is a `NativeArray` allocated with `Allocator.Temp` whose length is >10 elements. * Throw error when Entities.ForEach has an argument that is a generic DynamicBuffer. * Re-adding a system to a `ComponentSystemGroup` immediately after removing it from the group now works correctly. * `ComponentSystemGroup.Remove()` is now ignored if the target system is already enqueued for removal, or if it isn't in the group's update list in the first place. * Fixed IL post-processing warnings being emitted with "error" title. * Fixed "Invalid IL" error when try/finally block occurs in `Entities.ForEach` lambda body or cloned method (usually occurs with `using` or `foreach` and `WithoutBurst`). * Fixed `Unexpected error` when `Job.WithCode` is used with `WithStructuralChanges` (now throw an error). * Fixed a bug where `Unity.Scenes.EntityScenesPaths.GetTempCachePath()` could return invalid strings * Fixed freezing of editor due to accessing the `EntityManager` property within Rider's debugger * Fixed a bug where calling `SetArchetype` on an entity containing a component with `ISystemStateComponentData` may sometimes incorrectly throw an `ArgumentException` ### Known Issues * This version is not compatible with 2020.2.0a17. Please update to the forthcoming alpha. --- CHANGELOG.md | 89 +- DocCodeSamples.Tests/EntityQueryExamples.cs | 178 +- .../IJobEntityBatchExamples.cs | 2 +- Documentation~/chunk_iteration_job.md | 6 +- Documentation~/component_data.md | 2 +- Documentation~/custom_job_types.md | 2 +- Documentation~/ecs_entities_foreach.md | 2 +- Documentation~/ecs_entity_query.md | 211 +-- Documentation~/ecs_job_withcode.md | 2 +- Documentation~/ecs_systems.md | 4 +- Documentation~/gp_overview.md | 26 + Documentation~/manual_iteration.md | 2 +- .../LambdaJobsPostProcessorErrorTests.cs | 86 +- .../EntitiesILPostProcessor.cs | 36 +- .../ISystemBasePostProcessor.cs | 6 +- .../JobReflectionDataPostProcessor.cs | 251 +++ .../JobReflectionDataPostProcessor.cs.meta | 2 +- .../LambdaJobs/UserError.cs | 15 + .../SingletonAccessPostProcessor.cs | 22 +- .../StaticTypeRegistryPostProcessor.cs | 15 +- .../StaticTypeRegistry/TypeInfoGeneration.cs | 11 + Unity.Entities.CodeGen/TestCaseILPP.cs | 3 +- .../EntityDebuggerTests.cs | 5 +- Unity.Entities.Editor.Tests/ListViewTests.cs | 4 +- .../EntityDebugger/EntityDebugger.cs | 25 +- .../Unity.Entities.Editor.asmdef | 1 + .../Conversion/ConversionJournalDataTests.cs | 89 +- .../Conversion/MultiListTests.cs | 67 +- .../WorldScriptUpdateOrderTests.cs | 185 +- Unity.Entities.Hybrid/AssemblyInfo.cs | 1 + .../ConversionJournalData.cs | 108 +- .../GameObjectConversionMappingSystem.cs | 4 +- .../GameObjectConversionSettings.cs | 85 +- .../GameObjectConversionSystem.cs | 2 +- .../GameObjectConversionUtility.cs | 2 +- .../GameObjectConversion/JournalDataDebug.cs | 2 +- .../GameObjectConversion/MultiList.cs | 166 +- .../EntityCommandBufferPerformanceTests.cs | 56 +- ...QueryIncrementalCachingPerformanceTests.cs | 261 +++ ...IncrementalCachingPerformanceTests.cs.meta | 11 + .../BurstCompatibilityTests.gen.cs | 1648 ----------------- .../ComponentOrderVersionTests.cs | 5 +- Unity.Entities.Tests/ComponentSystemTests.cs | 18 - Unity.Entities.Tests/CreateAndDestroyTests.cs | 79 +- Unity.Entities.Tests/DisableComponentTests.cs | 4 +- Unity.Entities.Tests/ECSTestsFixture.cs | 2 + .../EmbeddedPackageOnlyTestAttribute.cs | 47 + .../EmbeddedPackageOnlyTestAttribute.cs.meta | 0 .../EntitiesBurstCompatibilityTests.cs | 17 + .../EntitiesBurstCompatibilityTests.cs.meta | 11 + .../EntityCommandBufferTests.cs | 57 +- ...ityManagerComponentGroupOperationsTests.cs | 154 ++ .../EntityManagerUnmanagedTests.cs | 1 + Unity.Entities.Tests/EntityQueryTests.cs | 327 +++- Unity.Entities.Tests/IJobChunkTests.cs | 2 + Unity.Entities.Tests/IJobEntityBatchTests.cs | 4 +- Unity.Entities.Tests/JobBasicTests.cs | 46 + .../SystemBaseSingletonAccessTests.cs | 20 + Unity.Entities.Tests/TestComponents.cs | 74 + .../Unity.Entities.Tests.asmdef | 8 +- Unity.Entities.Tests/WorldTests.cs | 92 - Unity.Entities/BurstCompatibleAttribute.cs | 20 - .../BurstCompatibleAttribute.cs.meta | 3 - Unity.Entities/ChunkDataUtility.cs | 14 +- Unity.Entities/ComponentSystem.cs | 4 - Unity.Entities/ComponentSystemBase.cs | 393 +--- Unity.Entities/ComponentSystemGroup.cs | 19 +- Unity.Entities/DebugView.cs | 4 +- Unity.Entities/DefaultWorldInitialization.cs | 112 +- .../Diff/EntityDifferCopyAndReplace.cs | 2 + Unity.Entities/ECBInterop.interop.gen.cs | 2 + Unity.Entities/EntityCommandBuffer.cs | 108 +- .../EntityCommandBuffer.interop.gen.cs | 2 + Unity.Entities/EntityCommandBufferSystem.cs | 2 - Unity.Entities/EntityComponentStore.cs | 115 +- ...tityComponentStoreCreateDestroyEntities.cs | 2 +- Unity.Entities/EntityComponentStoreDebug.cs | 44 +- Unity.Entities/EntityDataAccess.cs | 58 +- Unity.Entities/EntityManager.cs | 49 +- .../EntityManagerAccessComponentData.cs | 18 + .../EntityManagerChangeArchetype.cs | 183 +- ...ntityManagerChangeArchetype.interop.gen.cs | 2 + .../EntityManagerCreateArchetype.cs | 6 + .../EntityManagerCreateDestroyEntities.cs | 17 +- Unity.Entities/EntityManagerDebug.cs | 22 + ...EntityManagerExclusiveEntityTransaction.cs | 1 + Unity.Entities/EntityManagerMoveEntities.cs | 25 +- Unity.Entities/EntityManagerQuery.cs | 7 + Unity.Entities/EntityManagerSerialization.cs | 1 + Unity.Entities/EntityManagerTypes.cs | 5 + Unity.Entities/EntityManagerValidate.cs | 4 + Unity.Entities/EntityManagerVersions.cs | 3 + ...tScheduable.cs => IJobBurstSchedulable.cs} | 23 +- ...e.cs.meta => IJobBurstSchedulable.cs.meta} | 0 Unity.Entities/IJobChunk.cs | 135 +- Unity.Entities/IJobEntityBatch.cs | 171 +- ....cs => IJobParallelForBurstSchedulable.cs} | 26 +- ...> IJobParallelForBurstSchedulable.cs.meta} | 0 .../Iterators/ChunkDataGatherJobs.cs | 10 +- .../Iterators/ChunkIterationUtility.cs | 177 +- .../ChunkIterationUtility.interop.gen.cs | 126 ++ Unity.Entities/Iterators/DynamicBuffer.cs | 6 + Unity.Entities/Iterators/EntityQuery.cs | 19 + .../Iterators/EntityQueryManager.cs | 85 + Unity.Entities/JobComponentSystem.cs | 6 - Unity.Entities/ScriptBehaviourUpdateOrder.cs | 21 +- Unity.Entities/SystemBase.cs | 9 +- Unity.Entities/SystemBaseRegistry.cs | 2 +- Unity.Entities/SystemState.cs | 648 +++++++ Unity.Entities/SystemState.cs.meta | 11 + Unity.Entities/Types/Archetype.cs | 20 +- Unity.Entities/Types/ArchetypeChunkData.cs | 5 + .../Types/BurstCompatibleComponents.cs | 20 + .../Types/BurstCompatibleComponents.cs.meta | 11 + Unity.Entities/Types/ComponentType.cs | 8 +- Unity.Entities/Types/EarlyInitHelpers.cs | 51 + Unity.Entities/Types/EarlyInitHelpers.cs.meta | 11 + Unity.Entities/Types/EntityArchetype.cs | 11 + .../Types/RegisterGenericJobTypeAttribute.cs | 22 + .../RegisterGenericJobTypeAttribute.cs.meta | 11 + Unity.Entities/UnsafeList.gen.cs | 30 +- Unity.Entities/UnsafeList.tt | 12 +- Unity.Entities/World.cs | 127 +- .../EmbeddedPackageOnlyTestAttribute.cs | 36 - .../LiveLinkEditorConnectionTests.cs | 60 +- .../LiveLinkEditorTests.cs | 4 - .../SubSceneDeduplicationTests.cs | 302 +++ .../SubSceneDeduplicationTests.cs.meta | 11 + .../Unity.Scenes.Editor.Tests.asmdef | 2 + Unity.Scenes.Editor/EditorEntityScenes.cs | 31 +- .../LiveLinkAssetBundleBuildSystem.cs | 35 +- .../LiveLink/LiveLinkBuildImporter.cs | 6 - .../LiveLink/LiveLinkBuildPipeline.cs | 36 + Unity.Scenes.Editor/SceneMetaDataImporter.cs | 3 + Unity.Scenes.Editor/SubSceneBuildCode.cs | 361 +++- Unity.Scenes.Editor/SubSceneImporter.cs | 27 +- Unity.Scenes.Editor/SubSceneInspector.cs | 318 +++- .../SubSceneInspectorUtility.cs | 87 +- Unity.Scenes.Editor/TypeDependencyCache.cs | 5 + .../Unity.Scenes.Editor.asmdef | 5 +- Unity.Scenes/AssetBundleManager.cs | 81 +- Unity.Scenes/AsyncLoadSceneOperation.cs | 76 +- Unity.Scenes/EntityScenesPaths.cs | 24 +- .../LiveLinkPlayerAssetRefreshSystem.cs | 2 +- .../LiveLinkResolveSceneReferenceSystem.cs | 4 +- Unity.Scenes/ResolveSceneReferenceSystem.cs | 10 +- Unity.Scenes/ResolveSceneSectionUtility.cs | 77 +- Unity.Scenes/SceneSectionBundleTag.cs | 35 +- Unity.Scenes/SceneSectionStreamingSystem.cs | 18 +- Unity.Scenes/SubScene.cs | 8 +- package.json | 18 +- 151 files changed, 6095 insertions(+), 3311 deletions(-) create mode 100644 Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs rename Unity.Entities.Tests/BurstCompatibilityTests.gen.cs.meta => Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs.meta (83%) create mode 100644 Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs create mode 100644 Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs.meta delete mode 100644 Unity.Entities.Tests/BurstCompatibilityTests.gen.cs create mode 100644 Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs rename {Unity.Scenes.Editor.Tests => Unity.Entities.Tests}/EmbeddedPackageOnlyTestAttribute.cs.meta (100%) create mode 100644 Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs create mode 100644 Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs.meta delete mode 100644 Unity.Entities/BurstCompatibleAttribute.cs delete mode 100644 Unity.Entities/BurstCompatibleAttribute.cs.meta rename Unity.Entities/{IJobBurstScheduable.cs => IJobBurstSchedulable.cs} (86%) rename Unity.Entities/{IJobBurstScheduable.cs.meta => IJobBurstSchedulable.cs.meta} (100%) rename Unity.Entities/{IJobParallelForBurstScheduable.cs => IJobParallelForBurstSchedulable.cs} (85%) rename Unity.Entities/{IJobParallelForBurstScheduable.cs.meta => IJobParallelForBurstSchedulable.cs.meta} (100%) create mode 100644 Unity.Entities/SystemState.cs create mode 100644 Unity.Entities/SystemState.cs.meta create mode 100644 Unity.Entities/Types/BurstCompatibleComponents.cs create mode 100644 Unity.Entities/Types/BurstCompatibleComponents.cs.meta create mode 100644 Unity.Entities/Types/EarlyInitHelpers.cs create mode 100644 Unity.Entities/Types/EarlyInitHelpers.cs.meta create mode 100644 Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs create mode 100644 Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs.meta delete mode 100644 Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs create mode 100644 Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs create mode 100644 Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index b544c1ed..04fcce39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,46 @@ +# Change log + +## [0.14.0] - 2020-08-04 + +### Added + +* Added `IsEmpty` property to `DynamicBuffer`. +* Added deduplication for asset bundles generated for subscenes. +* Added new `EntityManager` methods: `AddComponent(EntityQuery, ComponentTypes)`, which adds multiple components to all entities matching a query; and `RemoveComponent(Entity, ComponentTypes)`, which removes multiple components from a single entity. (`AddComponent(Entity, ComponentTypes)` and `RemoveComponent(EntityQuery, ComponentTypes)` already existed. This patch just fills in a few 'missing' methods.) + +### Changed +* `IJobEntityBatch.ScheduleSingle` is being renamed to `IJobEntityBatch.Schedule` to match our naming guidelines for job scheduling. +* When `DefaultWorldInitialization.Initialize()` adds the default World's system groups to the Unity player loop, it now bases its modifications on the current player loop instead of the default player loop. This prevents the Entities package from accidentally erasing any previous player loop modifications made outside the package. +* `DefaultWorldInitialization.DomainUnloadOrPlayModeChangeShutdown()` now removes all existing `World`s from the Unity player loop before destroying them. If a `World` that was added to the player loop is destroyed manually prior to domain unload, it must also be removed from the player loop manually using `ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop()`. +* Updated package `com.unity.platforms` to version `0.7.0-preview.8`. +* `EntityManager.CreateEntity()`, `EntityManager.SetArchetype()`, and `EntityCommandBuffer.CreateEntity()` no longer accept the value returned by `new EntityArchetype()` because it's invalid. Same for `EntityCommandBuffer.CreateEntity()` and `EntityCommandBuffer.ParallelWriter.CreateEntity()`. Always use `EntityManager.CreateArchetype()` instead of `new EntityArchetype()` to create `EntityArchetype` values. (Ideally, the `EntityArchetype` constructor wouldn't be public, but C# doesn't allow that for a struct.) +* Subscene Inspector now uses a table format to allow easier management of multiple subscenes + +### Deprecated +* `IJobEntityBatch.ScheduleParallelBatched` is being deprecated in favor of adding a batching parameter to `IJobEntityBatch.ScheduleParallel` +* `ScriptBehaviourUpdateOrder.UpdatePlayerLoop()` is being deprecated in favor of `ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop()`. Due to slightly different semantics, a direct automated API update is not possible: the new function always takes a `PlayerLoopSystem` object to modify, does not call `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()`, and does not create the top-level system groups if they don't exist. +* `ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(World)` is being deprecated in favor of `ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(World)`. + +### Removed + +* Removed obsolete `ScriptBehaviourUpdateOrder.CurrentPlayerLoop`. Use `UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop()` instead. +* Removed obsolete `ScriptBehaviourUpdateOrder.SetPlayerLoop()`. Use `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()` instead. + +### Fixed + +* Setting the Scene Asset on a Subscene would sometimes fail to trigger an import/conversion because the default ECS world was missing. +* Fixed crash when using Singleton access methods (GetSingleton, SetSingleton, etc.) with a generic parameter as argument. +* Fixed an issue which caused WebGL not to work, and could produce this error message on IL2CPP-based backends: +``` +NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: Unity.Entities.SystemBase::UnmanagedUpdate + + ## [0.13.0] - 2020-07-10 ### Added -* Added new `EntityCommandBuffer` methods: `AddComponent(Entity, ComponentTypes)` and `AddComponent(EntityQuery, ComponentTypes)`. These add multiple components in one call. **Note:** `EntityManager` has an equivalent of the first and will get an equivalent of the second in a later release. -* Added new `EntityCommandBuffer` methods: `RemoveComponent(Entity, ComponentTypes)` and `RemoveComponent(EntityQuery, ComponentTypes)`. These remove multiple components in one call. **Note:** `EntityManager` will get equivalents in a future release. -* Added new `IJobEntityBatchWithIndex` job interface. This is a variant of `IJobEntityBatch` and provides an additional `indexOfFirstEntityInQuery` parameter, which provides a per-batch index that is the aggregate of all previous batch counts. +* Added new `EntityCommandBuffer` methods: `AddComponent(Entity, ComponentTypes)` and `AddComponent(EntityQuery, ComponentTypes)` for adding multiple components in one call. (`EntityManager` has an equivalent of the first already and will get an equivalent of the second later.) +* Added new `EntityCommandBuffer` methods: `RemoveComponent(Entity, ComponentTypes)` and `RemoveComponent(EntityQuery, ComponentTypes)` for removing multiple components in one call. (`EntityManager` will get equivalents in the future.) +* Added new `IJobEntityBatchWithIndex` job interface, a variant of `IJobEntityBatch` that provides an additional `indexOfFirstEntityInQuery` parameter, which provides a per-batch index that is the aggregate of all previous batch counts. * Added `MaximumDeltaTime` property to `FixedStepSimulationSystemGroup`, used similarly to `UnityEngine.Time.maximumDeltaTime` to control how gradually the application should recover from large transient frame time spikes. * Added new player loop management functions to the `ScriptBehaviourUpdateOrder` class: * `AppendSystemToPlayerLoopList()`: adds a single ECS system to a specific point in the Unity player loop. @@ -15,39 +52,31 @@ ### Changed * Updated minimum Unity Editor version to 2020.1.0b15 (40d9420e7de8) -* Changed the Profiler markers for `EntityCommandBuffer.Playback` from `EntityCommandBufferSystem`s to include the name of the system that recorded the `EntityCommandBuffer`. +* Profiler markers for `EntityCommandBuffer.Playback` from `EntityCommandBufferSystem`s now include name of the system that recorded the `EntityCommandBuffer`. * Bumped burst to 1.3.2 version. -* Changed the EntityQuery commands for `AddComponent`, `RemoveComponent`, and `DestroyEntity` in the EntityCommandBuffer to use Burst during Playback. -* Optimized `IJobChunk` and `Entities.ForEach` `ScheduleParallel` when there is no EntityQuery filtering necessary (shared component or change filtering). -* Changed `TypeManager.GetSystems()` to return an `IReadOnlyList` rather than a `List` -* Updated package `com.unity.platforms` to version `0.6.0-preview.1`. -* Changed `EntityContainer` to write data back to the entity. +* EntityQuery commands for `AddComponent`, `RemoveComponent`, and `DestroyEntity` in the EntityCommandBuffer now use Burst during Playback. +* IJobChunk and Entities.ForEach ScheduleParallel has been optimized in case there is no EntityQuery filtering necessary (Shared component or change filtering) +* `TypeManager.GetSystems()` now returns an `IReadOnlyList` rather than a `List` +* Updated package `com.unity.platforms` to version `0.6.0-preview.4`. +* `EntityContainer` will now allow to write data back to the entity. * Updated package `com.unity.properties` and `com.unity.serialization` to version `1.3.1-preview`. ### Fixed -* Fixed an issue where a warning was treated as an error when `Entities.ForEach` passed a component type as value. +* Fixed warning treated as error in the case that a warning is emitted for Entities.ForEach passing a component type as value. * Fixed paths displayed in IL post-processing error messages to be more consistent with Unity error messages. -* Fixed an issue where an exceptions was thrown when inspecting an entity with a GameObject added through `EntityManager.AddComponentObject`. -* Fixed a `DCICE002` error that was thrown during IL post-processing when `Entities.ForEach` contained multiple Entities.ForEach in the same scope capturing multiple variables. -* Fixed an issue where `EntityManager`'s `AddComponent()`, `RemoveComponent()`, and `CopyEntitiesFrom()` methods threw an error if their input was a `NativeArray` allocated with `Allocator.Temp` whose length is more than 10 elements. -* An error is thrown when Entities.ForEach has an argument that is a generic DynamicBuffer. -* Fixed an issue where re-adding a system to a `ComponentSystemGroup` immediately after removing it from the group did not work correctly. -* `ComponentSystemGroup.Remove()` is now ignored if the target system is already queued for removal, or if it isn't in the group's update list in the first place. -* Fixed an issue where IL post-processing warnings were emitted with "error" title. -* Fixed "Invalid IL" error when try/finally block happened in the `Entities.ForEach` lambda body or cloned method. This usually happened with `using` or `foreach` and `WithoutBurst`. -* Fixed an `Unexpected error` when `Job.WithCode` was used with `WithStructuralChanges`. This now throws an error. -* Fixed a bug where `Unity.Scenes.EntityScenesPaths.GetTempCachePath()` returned invalid strings. - -### Deprecated -* `ScriptBehaviourUpdateOrder.UpdatePlayerLoop()` is being deprecated in favor of `ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop()`. Due to slightly different semantics, a direct automated API update is not possible: the new function always takes a `PlayerLoopSystem` object to modify, does not call `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()`, and does not create the top-level system groups if they don't exist. -* `ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(World)` is being deprecated in favor of `ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(World)`. - - -### Removed -* Removed obsolete `ScriptBehaviourUpdateOrder.CurrentPlayerLoop`. Use `UnityEngine.LowLevel.PlayerLoop.GetCurrentPlayerLoop()` instead. -* Removed obsolete `ScriptBehaviourUpdateOrder.SetPlayerLoop()`. Use `UnityEngine.LowLevel.PlayerLoop.SetPlayerLoop()` instead. - +* Fixed exceptions being thrown when inspecting an entity with a GameObject added through `EntityManager.AddComponentObject`. +* Fixed `DCICE002` error thrown during IL post-processing when `Entities.ForEach` contains multiple Entities.ForEach in same scope capturing multiple variables. +* `EntityManager`'s `AddComponent()`, `RemoveComponent()`, and `CopyEntitiesFrom()` methods no longer throw an error if their input is a `NativeArray` allocated with `Allocator.Temp` whose length is >10 elements. +* Throw error when Entities.ForEach has an argument that is a generic DynamicBuffer. +* Re-adding a system to a `ComponentSystemGroup` immediately after removing it from the group now works correctly. +* `ComponentSystemGroup.Remove()` is now ignored if the target system is already enqueued for removal, or if it isn't in the group's update list in the first place. +* Fixed IL post-processing warnings being emitted with "error" title. +* Fixed "Invalid IL" error when try/finally block occurs in `Entities.ForEach` lambda body or cloned method (usually occurs with `using` or `foreach` and `WithoutBurst`). +* Fixed `Unexpected error` when `Job.WithCode` is used with `WithStructuralChanges` (now throw an error). +* Fixed a bug where `Unity.Scenes.EntityScenesPaths.GetTempCachePath()` could return invalid strings +* Fixed freezing of editor due to accessing the `EntityManager` property within Rider's debugger +* Fixed a bug where calling `SetArchetype` on an entity containing a component with `ISystemStateComponentData` may sometimes incorrectly throw an `ArgumentException` ### Known Issues diff --git a/DocCodeSamples.Tests/EntityQueryExamples.cs b/DocCodeSamples.Tests/EntityQueryExamples.cs index 20d16187..3b998f75 100644 --- a/DocCodeSamples.Tests/EntityQueryExamples.cs +++ b/DocCodeSamples.Tests/EntityQueryExamples.cs @@ -16,7 +16,9 @@ public struct Singlet : IComponentData public struct Melee : IComponentData {} public struct Ranger : IComponentData {} public struct Player : IComponentData {} - public struct Position : IComponentData {} + public struct Position : IComponentData { public float3 Value; } + public struct RotationQuaternion : IComponentData { public quaternion Value; } + public struct Displacement : IComponentData { public float3 Value; } public class EntityQueryExamples : SystemBase { @@ -86,5 +88,179 @@ protected override void OnUpdate() queryForSingleton.SetSingleton(new Singlet {Value = 1}); #endregion } + + void ManualExamples1() + { + { + #region define-query + EntityQuery query + = GetEntityQuery(typeof(RotationQuaternion), + ComponentType.ReadOnly()); + #endregion + } + { + #region query-desc + var queryDescription = new EntityQueryDesc + { + None = new ComponentType[] { typeof(Frozen) }, + All = new ComponentType[]{ typeof(RotationQuaternion), + ComponentType.ReadOnly() } + }; + EntityQuery query = GetEntityQuery(queryDescription); + #endregion + } + { + #region combine-query + var desc1 = new EntityQueryDesc + { + All = new ComponentType[] { typeof(RotationQuaternion) } + }; + + var desc2 = new EntityQueryDesc + { + All = new ComponentType[] { typeof(RotationSpeed) } + }; + + EntityQuery query + = GetEntityQuery(new EntityQueryDesc[] { desc1, desc2 }); + + #endregion + } + { + EntityManager entityManager = World.EntityManager; + #region create-query + EntityQuery query = + entityManager.CreateEntityQuery(typeof(RotationQuaternion), + ComponentType.ReadOnly()); + #endregion + } + + + } + } + #region query-writegroup + public struct C1 : IComponentData { } + + [WriteGroup(typeof(C1))] + public struct C2 : IComponentData { } + + [WriteGroup(typeof(C1))] + public struct C3 : IComponentData { } + + public class ECSSystem : SystemBase + { + protected override void OnCreate() + { + var queryDescription = new EntityQueryDesc + { + All = new ComponentType[] { ComponentType.ReadWrite(), + ComponentType.ReadOnly() }, + Options = EntityQueryOptions.FilterWriteGroup + }; + var query = GetEntityQuery(queryDescription); + } + + protected override void OnUpdate() + { + throw new NotImplementedException(); + } + } + #endregion + + #region get-query + public class RotationSpeedSys : SystemBase + { + private EntityQuery query; + + protected override void OnUpdate() + { + float deltaTime = Time.DeltaTime; + + Entities + .WithStoreEntityQueryInField(ref query) + .ForEach( + (ref RotationQuaternion rotation, in RotationSpeed speed) => { + rotation.Value + = math.mul( + math.normalize(rotation.Value), + quaternion.AxisAngle(math.up(), + speed.RadiansPerSecond * deltaTime) + ); + }) + .Schedule(); + } + } + #endregion + + #region get-query-ijobchunk + public class RotationSystem : SystemBase + { + private EntityQuery query; + + protected override void OnCreate() + { + query = GetEntityQuery(typeof(RotationQuaternion), + ComponentType.ReadOnly()); + } + + protected override void OnUpdate() + { + throw new NotImplementedException(); + } + } + #endregion + #region shared-component-filter + struct SharedGrouping : ISharedComponentData + { + public int Group; + } + + class ImpulseSystem : SystemBase + { + EntityQuery query; + + protected override void OnCreate() + { + query = GetEntityQuery(typeof(Position), + typeof(Displacement), + typeof(SharedGrouping)); + } + + protected override void OnUpdate() + { + // Only iterate over entities that have the SharedGrouping data set to 1 + query.SetSharedComponentFilter(new SharedGrouping { Group = 1 }); + + var positions = query.ToComponentDataArray(Allocator.Temp); + var displacements = query.ToComponentDataArray(Allocator.Temp); + + for (int i = 0; i < positions.Length; i++) + positions[i] = + new Position + { + Value = positions[i].Value + displacements[i].Value + }; + } + } + + #endregion + class UpdateSystem : SystemBase + { + #region change-filter + EntityQuery query; + + protected override void OnCreate() + { + query = GetEntityQuery(typeof(LocalToWorld), + ComponentType.ReadOnly()); + query.SetChangedVersionFilter(typeof(Translation)); + + } + #endregion + + protected override void OnUpdate() + { + throw new NotImplementedException(); + } } } diff --git a/DocCodeSamples.Tests/IJobEntityBatchExamples.cs b/DocCodeSamples.Tests/IJobEntityBatchExamples.cs index a66d908f..7d2fc4ac 100644 --- a/DocCodeSamples.Tests/IJobEntityBatchExamples.cs +++ b/DocCodeSamples.Tests/IJobEntityBatchExamples.cs @@ -76,7 +76,7 @@ protected override void OnUpdate() job.deltaTime = this.Time.DeltaTime; int batchesPerChunk = 4; // Partition each chunk into this many batches. Each batch will be processed concurrently. - this.Dependency = job.ScheduleParallelBatched(query, batchesPerChunk, this.Dependency); + this.Dependency = job.ScheduleParallel(query, batchesPerChunk, this.Dependency); } } #endregion diff --git a/Documentation~/chunk_iteration_job.md b/Documentation~/chunk_iteration_job.md index e1f8ce46..684604bd 100644 --- a/Documentation~/chunk_iteration_job.md +++ b/Documentation~/chunk_iteration_job.md @@ -21,7 +21,7 @@ For more information, the [ECS samples repository](https://github.com/Unity-Tech -## Query for data with a EntityQuery +## Query for data with an EntityQuery An EntityQuery defines the set of component types that an archetype must contain for the system to process its associated chunks and entities. An archetype can have additional components, but it must have at least those that the EntityQuery defines. You can also exclude archetypes that contain specific types of components. @@ -94,11 +94,11 @@ __Note:__ If you use a concurrent entity command buffer, pass the `chunkIndex` a ## Skipping chunks with unchanged entities -If you only need to update entities when a component value has changed, you can add that component type to the change filter of the EntityQuery that selects the entities and chunks for the job. For example, if you have a system that reads two components and only needs to update a third when one of the first two has changed, you can use a EntityQuery as follows: +If you only need to update entities when a component value has changed, you can add that component type to the change filter of the EntityQuery that selects the entities and chunks for the job. For example, if you have a system that reads two components and only needs to update a third when one of the first two has changed, you can use an EntityQuery as follows: [!code-cs[changefilter](../DocCodeSamples.Tests/ChunkIterationJob.cs#changefilter)] -The EntityQuery change filter supports up to two components. If you want to check more or you aren't using a EntityQuery, you can make the check manually. To make this check, use the `ArchetypeChunk.DidChange()` function to compare the chunk’s change version for the component to the system's `LastSystemVersion`. If this function returns false, you can skip the current chunk altogether because none of the components of that type have changed since the last time the system ran. +The EntityQuery change filter supports up to two components. If you want to check more or you aren't using an EntityQuery, you can make the check manually. To make this check, use the `ArchetypeChunk.DidChange()` function to compare the chunk’s change version for the component to the system's `LastSystemVersion`. If this function returns false, you can skip the current chunk altogether because none of the components of that type have changed since the last time the system ran. You must use a struct field to pass the `LastSystemVersion` from the system into the job, as follows: diff --git a/Documentation~/component_data.md b/Documentation~/component_data.md index 798ddd7f..e9913bfe 100644 --- a/Documentation~/component_data.md +++ b/Documentation~/component_data.md @@ -29,7 +29,7 @@ It is helpful to use a managed `IComponentData` (that is, `IComponentData` decla These components are used the same way as value type `IComponentData`. However, ECS handles them internally in a much different (and slower) way. If you don't need managed component support, define `UNITY_DISABLE_MANAGED_COMPONENTS` in your application's __Player Settings__ (menu: __Edit > Project Settings > Player > Scripting Define Symbols__) to prevent accidental usage. -Because managed `IComponentData` is a managed type, it has the following performance drawbacks compared to valuetype `IComponentData`: +Because managed `IComponentData` is a managed type, it has the following performance drawbacks compared to value-type `IComponentData`: * It cannot be used with the Burst Compiler * It cannot be used in job structs * It cannot use [Chunk memory](chunk_iteration.md) diff --git a/Documentation~/custom_job_types.md b/Documentation~/custom_job_types.md index 7a424cd5..626f2d03 100644 --- a/Documentation~/custom_job_types.md +++ b/Documentation~/custom_job_types.md @@ -252,7 +252,7 @@ counter.Count = 0; var handle = jobData.Schedule(input.Length, 8); handle.Complete(); -Debug.Log("The array countains " + counter.Count + " zeros"); +Debug.Log("The array contains " + counter.Count + " zeros"); counter.Dispose(); ``` diff --git a/Documentation~/ecs_entities_foreach.md b/Documentation~/ecs_entities_foreach.md index eadd586c..fdd5a190 100644 --- a/Documentation~/ecs_entities_foreach.md +++ b/Documentation~/ecs_entities_foreach.md @@ -16,7 +16,7 @@ Note the use of the keywords `ref` and `in` on the parameters of the ForEach lam ## Selecting entities -[Entities.ForEach] provides its own mechanism for defining the entity query used to select the entites to process. The query automatically includes any components you use as parameters of your lambda function. You can also use the `WithAll`, `WithAny`, and `WithNone` clauses to further refine which entities are selected. See [SystemBase.Entities] for the complete list of query options. +[Entities.ForEach] provides its own mechanism for defining the entity query used to select the entities to process. The query automatically includes any components you use as parameters of your lambda function. You can also use the `WithAll`, `WithAny`, and `WithNone` clauses to further refine which entities are selected. See [SystemBase.Entities] for the complete list of query options. The following example selects entities that have the components, Destination, Source, and LocalToWorld; and have at least one of the components, Rotation, Translation, or Scale; but which do not have a LocalToParent component. diff --git a/Documentation~/ecs_entity_query.md b/Documentation~/ecs_entity_query.md index 72990cfd..aba564c5 100644 --- a/Documentation~/ecs_entity_query.md +++ b/Documentation~/ecs_entity_query.md @@ -1,36 +1,33 @@ --- uid: ecs-entity-query --- -# Using a EntityQuery to query data +# Using an EntityQuery to query data -To read or write data, you must first find the data you want to change. The data in ECS is stored in components, which ECS groups together in memory according to the archetype of the entity to which they belong. You can use an [EntityQuery](xref:Unity.Entities.EntityQuery) to get a view of the ECS data that contains only the specific data you need for a given algorithm or process. +To read or write data, you must first find the data you want to change. The data in ECS is stored in components, which ECS groups together in memory according to the archetype of the entity to which they belong. You can use an [EntityQuery] to get a view of the ECS data that contains only the specific data you need for a given algorithm or process. -You can use an EntityQuery to do the following: +You can use an [EntityQuery] to do the following: -* Run a job to process the entities and components selected for the view +* Run a job to process the entities and components selected * Get a NativeArray that contains all of the selected entities * Get NativeArrays of the selected components (by component type) -The entity and component arrays an EntityQuery returns are guaranteed to be "parallel", that is, the same index value always applies to the same entity in any array. +The entity and component arrays an [EntityQuery] returns are guaranteed to be "parallel", that is, the same index value always applies to the same entity in any array. -**Note:** The `SystemBase.Entities.ForEach` constructions create internal EntityQuery instances based on the component types and attributes you specify for these APIs. You cannot use a different EntityQuery object with `Entities.ForEach`, (though you can get the query object that an `Entities.ForEach` instance constructs and use it elsewhere). +**Note:** The SystemBase [Entities.ForEach] constructions create internal [EntityQuery] instances based on the component types and attributes you specify for these APIs. You cannot use a different [EntityQuery] object with [Entities.ForEach], (though you can get the query object that an [Entities.ForEach] instance constructs and use it elsewhere). ## Defining a query -An EntityQuery query defines the set of component types that an archetype must contain for ECS to include its chunks and entities in the view. You can also exclude archetypes that contain specific types of components. +An [EntityQuery] query defines the set of component types that an archetype must contain for ECS to include its chunks and entities in the view. You can also exclude archetypes that contain specific types of components. -For simple queries, you can create a EntityQuery based on an array of component types. The following example defines a EntityQuery that finds all entities with both RotationQuaternion and RotationSpeed components. +For simple queries, you can create an [EntityQuery] based on an array of component types. The following example defines an [EntityQuery] that finds all entities with both RotationQuaternion and RotationSpeed components. -``` c# -EntityQuery m_Query = GetEntityQuery(typeof(RotationQuaternion), - ComponentType.ReadOnly()); -``` +[!code-cs[define-query](../DocCodeSamples.Tests/EntityQueryExamples.cs#define-query)] -The query uses `ComponentType.ReadOnly` instead of the simpler `typeof` expression to designate that the system does not write to RotationSpeed. Always specify read only when possible, because there are fewer constraints on read access to data, which can help the job scheduler execute the jobs more efficiently. +The query uses [ComponentType.ReadOnly] instead of the simpler [typeof] expression to designate that the system does not write to RotationSpeed. Always specify read only when possible, because there are fewer constraints on read access to data, which can help the job scheduler execute the jobs more efficiently. ### EntityQueryDesc -For more complex queries, you can use an EntityQueryDesc object to create the EntityQuery. An EntityQueryDesc provides a flexible query mechanism to specify which archetypes to select based on the following sets of components: +For more complex queries, you can use an [EntityQueryDesc] object to create the [EntityQuery]. An [EntityQueryDesc] provides a flexible query mechanism to specify which archetypes to select based on the following sets of components: * `All`: All component types in this array must exist in the archetype * `Any`: At least one of the component types in this array must exist in the archetype @@ -38,21 +35,14 @@ For more complex queries, you can use an EntityQueryDesc object to create the En For example, the following query includes archetypes that contain the RotationQuaternion and RotationSpeed components, but excludes any archetypes that contain the Frozen component: -``` c# -var query = new EntityQueryDesc -{ - None = new ComponentType[]{ typeof(Frozen) }, - All = new ComponentType[]{ typeof(RotationQuaternion), - ComponentType.ReadOnly() } -} -EntityQuery m_Query = GetEntityQuery(query); -``` +[!code-cs[query-desc](../DocCodeSamples.Tests/EntityQueryExamples.cs#query-desc)] -**Note:** Do not include optional components in the EntityQueryDesc. To handle optional components, use the `ArchetypeChunk.Has()` method to determine whether a chunk contains the optional component or not. Because all entities within the same chunk have the same components, you only need to check whether an optional component exists once per chunk: not once per entity. +**Note:** Do not include optional components in the [EntityQueryDesc]. To handle optional components, use the [ArchetypeChunk.Has] method to determine whether a chunk contains the optional component or not. Because all entities within the same chunk have the same components, you only need to check whether an optional component exists once per chunk: not once per entity. + ### Query options -When you create an EntityQueryDesc, you can set its `Options` variable. The options allow for specialized queries (normally you do not need to set them): +When you create an [EntityQueryDesc], you can set its `Options` variable. The options allow for specialized queries (normally you do not need to set them): * Default: No options set; the query behaves normally. * `IncludePrefab`: Includes archetypes that contain the special Prefab tag component. @@ -63,22 +53,7 @@ When you set the `FilterWriteGroup` option, only entities with those components In the following example, C2 and C3 are components in the same Write Group based on C1, and this query uses the FilterWriteGroup option that requires C1 and C3: -``` c# -public struct C1: IComponentData{} - -[WriteGroup(C1)] -public struct C2: IComponentData{} - -[WriteGroup(C1)] -public struct C3: IComponentData{} - -// ... In a system: -var query = new EntityQueryDesc{ - All = new ComponentType{typeof(C1), ComponentType.ReadOnly()}, - Options = EntityQueryDescOptions.FilterWriteGroup -}; -var m_Query = GetEntityQuery(query); -``` +[!code-cs[query-writegroup](../DocCodeSamples.Tests/EntityQueryExamples.cs#query-writegroup)] This query excludes any entities with both C2 and C3 because C2 is not explicitly included in the query. While you can use `None` to design this into the query, doing it through a Write Group provides an important benefit: you don't need to change the queries other systems use (as long as these systems also use Write Groups). @@ -88,147 +63,79 @@ For more information, see [Write Groups](ecs_write_groups.md). ### Combining queries -To combine multiple queries, you can pass an array of EntityQueryDesc objects rather than a single instance. You must use a logical OR operation to combine each query. The following example selects any archetypes that contain a RotationQuaternion component or a RotationSpeed component (or both): +To combine multiple queries, you can pass an array of [EntityQueryDesc] objects rather than a single instance. You must use a logical OR operation to combine each query. The following example selects any archetypes that contain a RotationQuaternion component or a RotationSpeed component (or both): -```c# -var query0 = new EntityQueryDesc -{ - All = new ComponentType[] {typeof(RotationQuaternion)} -}; +[!code-cs[combine-query](../DocCodeSamples.Tests/EntityQueryExamples.cs#combine-query)] -var query1 = new EntityQueryDesc -{ - All = new ComponentType[] {typeof(RotationSpeed)} -}; +## Creating an EntityQuery -EntityQuery m_Query - = GetEntityQuery(new EntityQueryDesc[] {query0, query1}); -``` +Outside of a system class, you can create an [EntityQuery] with the [EntityManager.CreateEntityQuery] function as follows: -## Creating a EntityQuery +[!code-cs[create-query](../DocCodeSamples.Tests/EntityQueryExamples.cs#create-query)] -Outside of a system class, you can create a EntityQuery with the `EntityManager.CreateEntityQuery()` function as follows: +However, inside a system class, you get a query from the system rather than creating it from scratch. Systems cache any queries that your implementation creates and return the cached instance rather than creating a new one when possible. -``` c# -EntityQuery m_Query = CreateEntityQuery(typeof(RotationQuaternion), - ComponentType.ReadOnly()); -``` +When your system uses [Entities.ForEach], use [WithStoreEntityQueryInField] to get an instance of the query used by an [Entities.ForEach] construction: -However, in a system class, you must use the `GetEntityQuery()` function for use with an IJobChunk job: +[!code-cs[get-query](../DocCodeSamples.Tests/EntityQueryExamples.cs#get-query)] -``` c# -public class RotationSpeedSystem : SystemBase -{ - private EntityQuery m_Query; - protected override void OnCreate() - { - m_Query = GetEntityQuery(typeof(RotationQuaternion), - ComponentType.ReadOnly()); - } - //… -} -``` +In other cases, such as when you need an instance of a query to schedule an IJobChunk job, use the [GetEntityQuery] function: -If you plan to reuse the same view, cache the EntityQuery instance, instead of creating a new one for each use. For example, in a system, you can create the EntityQuery in the system’s `OnCreate()` function and store the result in an instance variable. The `m_Query` variable in the above example is used for this purpose. +[!code-cs[get-query-ijobchunk](../DocCodeSamples.Tests/EntityQueryExamples.cs#get-query-ijobchunk)] -Note that queries created for a system are cached by the system. `GetEntityQuery()` returns the existing query if one already exists rather than creating a new one. However, filter settings are not considered when evaluating whether two queries are the same. In additon, if you set filters on a query, the same filters are set the next time you access that same query with `GetEntityQuery()`. Use `ResetFilter()` to clear the existing filters. +Note that filter settings are not considered when caching queries. In addition, if you set filters on a query, the same filters are set the next time you access that same query with [GetEntityQuery]. Use [ResetFilter] to clear any existing filters. ## Defining filters -You can filter the view as well as defining which components must be included or excluded from the query. You can specify the following types of filters: +Filters exclude entities that otherwise would be included among those returned by a query based on the following: * **Shared component filter**: Filter the set of entities based on specific values of a shared component. * **Change filter**: Filter the set of entities based on whether the value of a specific component type has changed. -The filters you set remain in effect until you call `ResetFilter()` on the query object. +The filters you set remain in effect until you call [ResetFilter] on the query object. + +**Note:** Write Groups use a different mechanism. See [Query options]. ### Shared component filters -To use a shared component filter, include the shared component in the EntityQuery (along with other needed components), and then call the `SetFilter()` function. Then pass in a struct of the same ISharedComponent type that contains the values to select. All values must match. You can add up to two different shared components to the filter. +To use a shared component filter, include the shared component in the [EntityQuery] -- along with other needed components -- and call the [SetSharedComponentFilter] function. Then pass in a struct of the same ISharedComponent type that contains the values to select. All values must match. You can add up to two different shared components to the filter. -You can change the filter at any time, but if you change the filter, it does not change any existing arrays of entities or components that you received from the group `ToComponentDataArray()` or `ToEntityArray()` functions. You must recreate these arrays. +You can change the filter at any time, but if you change the filter, it does not change any existing arrays of entities or components that you received from the group [ToComponentDataArray] or [ToEntityArray] functions. You must recreate these arrays. The following example defines a shared component named SharedGrouping and a system that only processes entities that have the Group field set to `1`. -```c# -struct SharedGrouping : ISharedComponentData -{ - public int Group; -} - -class ImpulseSystem : SystemBase -{ - EntityQuery m_Query; - - protected override void OnCreate(int capacity) - { - m_Query = GetEntityQuery(typeof(Position), - typeof(Displacement), - typeof(SharedGrouping)); - } - - protected override void OnUpdate() - { - // Only iterate over entities that have the SharedGrouping data set to 1 - m_Query.SetFilter(new SharedGrouping { Group = 1 }); - - var positions = m_Query.ToComponentDataArray(Allocator.Temp); - var displacememnts = m_Query.ToComponentDataArray(Allocator.Temp); - - for (int i = 0; i != positions.Length; i++) - positions[i].Value = positions[i].Value + displacememnts[i].Value; - } -} -``` +[!code-cs[shared-component-filter](../DocCodeSamples.Tests/EntityQueryExamples.cs#shared-component-filter)] ### Change filters -If you only need to update entities when a component value has changed, you can add that component to the EntityQuery filter using the `SetFilterChanged()` function. For example, the following EntityQuery only includes entities from chunks that another system has already written to the Translation component: - -``` c# -protected override void OnCreate(int capacity) -{ - m_Query = GetEntityQuery(typeof(LocalToWorld), - ComponentType.ReadOnly()); - m_Query.SetFilterChanged(typeof(Translation)); -} +If you only need to update entities when a component value has changed, you can add that component to the [EntityQuery] filter using the [SetFilterChanged] function. For example, the following [EntityQuery] only includes entities from chunks that another system has already written to the Translation component: -``` +[!code-cs[change-filter](../DocCodeSamples.Tests/EntityQueryExamples.cs#change-filter)] **Note:** For efficiency, the change filter applies to whole chunks, not individual entities. The change filter also only checks whether a system has run that declared write access to the component, not whether it actually changed any data. In other words, if another job which had the ability to write to that type of component accesses the chunk, then the change filter includes all entities in that chunk. This is why you should always declare read only access to components that you do not need to modify. ## Executing the query -An EntityQuery executes its query when you use the EntityQuery in a job or you call one of the EntityQuery methods that returns arrays of entities, components, or chunks in the view: - -* `ToEntityArray()` returns an array of the selected entities. -* `ToComponentDataArray` returns an array of the components of type `T` for the selected entities. -* `CreateArchetypeChunkArray()` returns all of the chunks that contain the selected entities. Because a query operates on archetypes, shared component values, and change filters, which are all identical for all the entities in a chunk, the set of entities stored win the returned set of chunks is exactly the same as the set of entities `ToEntityArray()` returns . - - - -### In jobs - -In a system that schedules an IJobChunk job, pass the EntityQuery object to the job's `ScheduleParallel()` or `ScheduleSingle()` methods. In the following example, from the HelloCube IJobChunk sample, the `m_Query` argument is the EntityQuery object - -``` c# -// OnUpdate runs on the main thread. -protected override void OnUpdate() -{ - var rotationType - = GetArchetypeChunkComponentType(false); - var rotationSpeedType - = GetArchetypeChunkComponentType(true); - - var job = new RotationSpeedJob() - { - RotationType = rotationType, - RotationSpeedType = rotationSpeedType, - DeltaTime = Time.deltaTime - }; - - return job.ScheduleParallel(m_Query, this.Dependency); -} -``` - -An EntityQuery uses jobs internally to create the required arrays. When you pass the group to one of the `Schedule()` methods, ECS schedules the EntityQuery jobs along with the system's own jobs and as such you can take advantage of parallel processing. +Typically, you "execute" a query when you schedule a job that uses it. +You can also call one of the [EntityQuery] methods that returns arrays of entities, components, or chunks: + +* [ToEntityArray] returns an array of the selected entities. +* [ToComponentDataArray] returns an array of the components of type `T` for the selected entities. +* [CreateArchetypeChunkArray] returns all of the chunks that contain the selected entities. Because a query operates on archetypes, shared component values, and change filters, which are all identical for all the entities in a chunk, the set of entities stored win the returned set of chunks is exactly the same as the set of entities [ToEntityArray] returns . + +[Query options]: #query-options +[EntityQuery]: xref:Unity.Entities.EntityQuery +[Entities.ForEach]: xref:ecs-entities-foreach +[typeof]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#typeof-operator +[EntityQueryDesc]: xref:Unity.Entities.EntityQueryDesc +[EntityManager.CreateEntityQuery]: xref:Unity.Entities.EntityManager.CreateEntityQuery* +[WithStoreEntityQueryInField]: xref:Unity.Entities.SystemBase.Entities +[GetEntityQuery]: xref:Unity.Entities.ComponentSystemBase.GetEntityQuery +[ResetFilter]: xref:Unity.Entities.EntityQuery.ResetFilter +[SetSharedComponentFilter]: xref:Unity.Entities.EntityQuery.SetSharedComponentFilter +[ToComponentDataArray]: xref:Unity.Entities.EntityQuery.ToComponentDataArray() +[ToEntityArray]: xref:Unity.Entities.EntityQuery.ToEntityArray +[CreateArchetypeChunkArray]: xref:Unity.Entities.EntityQuery.CreateArchetypeChunkArray +[SetFilterChanged]: xref:Unity.Entities.EntityQuery.SetFilterChanged +[ComponentType.ReadOnly]: xref:Unity.Entities.ComponentType.ReadOnly* +[ArchetypeChunk.Has]: xref:Unity.Entities.ArchetypeChunk.Has* diff --git a/Documentation~/ecs_job_withcode.md b/Documentation~/ecs_job_withcode.md index ececdd5b..f2b3a4fb 100644 --- a/Documentation~/ecs_job_withcode.md +++ b/Documentation~/ecs_job_withcode.md @@ -21,7 +21,7 @@ When you schedule your job to run in the [C# Job System] using `Schedule()`, the * Captured variables must be declared as [NativeArray] -- or other [native container] -- or a [blittable] type. * To return data, you must write the return value to a captured [native array], even if the data is a single value. (Note that you can write to any captured variable when executing with `Run()`.) -[Job.WithCode] provides a set of functions to apply read-only and safety attributes to your captured [native container] variables. For example, you can use `WithReadOnly` to designate that you don't update the container and `WithDisposeOnCompletion` to automatically dispose a container after the job finshes. ([Entities.ForEach] provides the same functions.) +[Job.WithCode] provides a set of functions to apply read-only and safety attributes to your captured [native container] variables. For example, you can use `WithReadOnly` to designate that you don't update the container and `WithDisposeOnCompletion` to automatically dispose a container after the job finishes. ([Entities.ForEach] provides the same functions.) See [Job.WithCode] for more information about these modifiers and attributes. diff --git a/Documentation~/ecs_systems.md b/Documentation~/ecs_systems.md index cecaf632..729d03f2 100644 --- a/Documentation~/ecs_systems.md +++ b/Documentation~/ecs_systems.md @@ -21,7 +21,7 @@ You can view the system configuration using the Entity Debugger window (menu: ** Unity ECS provides several types of systems. In general, the systems you write to implement your game behavior and data transformations will extend [SystemBase]. The other system classes have specialized purposes. You typically use existing instances of the [EntityCommandBufferSystem] and [ComponentSystemGroup] classes. * [SystemBase] -- the base class to implement when creating systems. -* [EntityCommandBufferSystem] -- provides [EntityCommandBuffer] instances for other systems. Each of the default system groups maintains an Entity Command Buffer System at the beginning and end of its list of child systems. This allows you to group structural changes so that they incur fewer [syncronization points] in a frame. +* [EntityCommandBufferSystem] -- provides [EntityCommandBuffer] instances for other systems. Each of the default system groups maintains an Entity Command Buffer System at the beginning and end of its list of child systems. This allows you to group structural changes so that they incur fewer [synchronization points] in a frame. * [ComponentSystemGroup] -- provides nested organization and update order for other systems. Unity ECS creates several Component System Groups by default. * [GameObjectConversionSystem] -- converts GameObject-based, in-Editor representations of your game to efficient, entity-based, runtime representations. Game conversion systems run in the Unity Editor. @@ -39,7 +39,7 @@ Unity ECS provides several types of systems. In general, the systems you write t [OnStartRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStartRunning* [OnStopRunning()]: xref:Unity.Entities.ComponentSystemBase.OnStopRunning* [OnUpdate()]: xref:Unity.Entities.SystemBase.OnUpdate* -[syncronization points]: xref:sync-points +[synchronization points]: xref:sync-points [system attributes]: system_update_order.md#attributes [SystemBase]: xref:Unity.Entities.SystemBase [World]: xref:Unity.Entities.World diff --git a/Documentation~/gp_overview.md b/Documentation~/gp_overview.md index 5bf42857..a2c21039 100644 --- a/Documentation~/gp_overview.md +++ b/Documentation~/gp_overview.md @@ -11,6 +11,32 @@ The systems include: * [Unity.Transforms](xref:Unity.Transforms): Provides components to define world-space transforms, 3D object hierarchies, and systems to manage them. * [Unity.Hybrid.Renderer](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@latest): Provides components and systems to render ECS entities in the Unity runtime. +## Gameplay supporting packages + +Some gameplay features in DOTS require additional packages to support them. For the list of features that require additional packages, see the table below. + +| **Feature** | **Packages** | +| --------------------------------- | ------------------------------------------------------------ | +| **DOTS ECS** | [com.unity.entities](https://docs.unity3d.com/Packages/com.unity.entities@latest) | +| **Rendering** | [com.unity.rendering.hybrid](https://docs.unity3d.com/Packages/com.unity.rendering.hybrid@latest) | +| **- Hybrid Renderer V2** | [com.unity.render-pipelines.high-definition](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest) or [com.unity.render-pipelines.universal](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest) | +| **- Animation** | [com.unity.animation](https://docs.unity3d.com/Packages/com.unity.animation@latest) | +| **Audio** | [com.unity.audio.dspgraph](https://docs.unity3d.com/Packages/com.unity.audio.dspgraph@latest) | +| **Physics** | [com.unity.physics](https://docs.unity3d.com/Packages/com.unity.physics@latest) or [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **- Smooth Penetration Recovery** | [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **- Stable Object Stacking** | [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **- Remove Speculative Contacts** | [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **- Rigidbody Sleeping** | [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **- Visual Debugger** | [com.havok.physics](https://docs.unity3d.com/Packages/com.havok.physics@latest) | +| **Multiplayer** | [com.unity.netcode](https://docs.unity3d.com/Packages/com.unity.netcode@latest) | +| **- Lag Compensation** | [com.unity.physics](https://docs.unity3d.com/Packages/com.unity.physics@latest) | +| **Project Building** | [com.unity.platforms](https://docs.unity3d.com/Packages/com.unity.platforms@latest) | +| **- Android** | [com.unity.platforms.android](https://docs.unity3d.com/Packages/com.unity.platforms.android@latest) | +| **- Linux** | [com.unity.platforms.linux](https://docs.unity3d.com/Packages/com.unity.platforms.linux@latest) | +| **- macOS** | [com.unity.platforms.macos](https://docs.unity3d.com/Packages/com.unity.platforms.macos@latest) | +| **- Web** | [com.unity.platforms.web](https://docs.unity3d.com/Packages/com.unity.platforms.web@latest) | +| **- Windows** | [com.unity.platforms.windows](https://docs.unity3d.com/Packages/com.unity.platforms.windows@latest) | + ## Authoring overview You can use the Unity Editor (with the required DOTS packages) to create DOTS-based games. In the Editor, you use GameObjects as normal to author a Scene and the ECS code converts the GameObjects to entities. diff --git a/Documentation~/manual_iteration.md b/Documentation~/manual_iteration.md index 6ea3389d..d5fdd322 100644 --- a/Documentation~/manual_iteration.md +++ b/Documentation~/manual_iteration.md @@ -1,6 +1,6 @@ # Manual iteration -You can request all of the chunks explicitly in a NativeArray and process them with a job such as `IJobParallelFor`. You should use this method if you need to manage chunks in a way that is not appropriate for the simplified model of iterating over all of the chunks in a EntityQuery. The following is an example of this: +You can request all of the chunks explicitly in a NativeArray and process them with a job such as `IJobParallelFor`. You should use this method if you need to manage chunks in a way that is not appropriate for the simplified model of iterating over all of the chunks in an EntityQuery. The following is an example of this: ```c# public class RotationSpeedSystem : SystemBase diff --git a/Unity.Entities.CodeGen.Tests/LambdaJobsPostProcessorErrorTests.cs b/Unity.Entities.CodeGen.Tests/LambdaJobsPostProcessorErrorTests.cs index d5618144..830fa15a 100644 --- a/Unity.Entities.CodeGen.Tests/LambdaJobsPostProcessorErrorTests.cs +++ b/Unity.Entities.CodeGen.Tests/LambdaJobsPostProcessorErrorTests.cs @@ -984,11 +984,26 @@ struct StructWithPrimitiveType } [Test] - public void ReadOnlyWarnsAboutArgumentType() + public void ReadOnlyWarnsAboutArgumentType_CorrectReadOnlyUsageWithNativeContainer() { AssertProducesNoError(typeof(CorrectReadOnlyUsageWithNativeContainer)); + } + + [Test] + public void ReadOnlyWarnsAboutArgumentType_CorrectReadOnlyUsageWithStruct() + { AssertProducesNoError(typeof(CorrectReadOnlyUsageWithStruct)); + } + + [Test] + public void ReadOnlyWarnsAboutArgumentType_IncorrectReadOnlyUsageWithStruct() + { AssertProducesError(typeof(IncorrectReadOnlyUsageWithStruct), nameof(UserError.DC0034), "structWithPrimitiveType"); + } + + [Test] + public void ReadOnlyWarnsAboutArgumentType_IncorrectReadOnlyUsageWithPrimitiveType() + { AssertProducesError(typeof(IncorrectReadOnlyUsageWithPrimitiveType), nameof(UserError.DC0034), "myVar"); } @@ -1038,11 +1053,23 @@ void Test() } [Test] - public void DeallocateOnJobCompletionWarnsAboutArgumentType() + public void DeallocateOnJobCompletionWarnsAboutArgumentType_CorrectDeallocateOnJobCompletionUsageWithNativeContainer() { AssertProducesNoError(typeof(CorrectDeallocateOnJobCompletionUsageWithNativeContainer)); + } + [Test] + public void DeallocateOnJobCompletionWarnsAboutArgumentType_CorrectDeallocateOnJobCompletionUsageWithStruct() + { AssertProducesNoError(typeof(CorrectDeallocateOnJobCompletionUsageWithStruct)); + } + [Test] + public void DeallocateOnJobCompletionWarnsAboutArgumentType_IncorrectDeallocateOnJobCompletionUsageWithStruct() + { AssertProducesError(typeof(IncorrectDeallocateOnJobCompletionUsageWithStruct), nameof(UserError.DC0035), "structWithPrimitiveType"); + } + [Test] + public void DeallocateOnJobCompletionWarnsAboutArgumentType_IncorrectDeallocateOnJobCompletionUsageWithPrimitiveType() + { AssertProducesError(typeof(IncorrectDeallocateOnJobCompletionUsageWithPrimitiveType), nameof(UserError.DC0035), "myVar"); } @@ -1095,11 +1122,23 @@ void Test() #pragma warning restore 0618 [Test] - public void DisableContainerSafetyRestrictionWarnsAboutArgumentType() + public void DisableContainerSafetyRestrictionWarnsAboutArgumentType_CorrectDisableContainerSafetyRestrictionUsageWithNativeContainer() { AssertProducesNoError(typeof(CorrectDisableContainerSafetyRestrictionUsageWithNativeContainer)); + } + [Test] + public void DisableContainerSafetyRestrictionWarnsAboutArgumentType_CorrectDisableContainerSafetyRestrictionUsageWithStruct() + { AssertProducesNoError(typeof(CorrectDisableContainerSafetyRestrictionUsageWithStruct)); + } + [Test] + public void DisableContainerSafetyRestrictionWarnsAboutArgumentType_IncorrectDisableContainerSafetyRestrictionUsageWithStruct() + { AssertProducesError(typeof(IncorrectDisableContainerSafetyRestrictionUsageWithStruct), nameof(UserError.DC0036), "structWithPrimitiveType"); + } + [Test] + public void DisableContainerSafetyRestrictionWarnsAboutArgumentType_IncorrectDisableContainerSafetyRestrictionUsageWithPrimitiveType() + { AssertProducesError(typeof(IncorrectDisableContainerSafetyRestrictionUsageWithPrimitiveType), nameof(UserError.DC0036), "myVar"); } @@ -1154,11 +1193,23 @@ void Test() } [Test] - public void DisableParallelForRestrictionWarnsAboutArgumentType() + public void DisableParallelForRestrictionWarnsAboutArgumentType_CorrectDisableParallelForRestrictionUsageWithNativeContainer() { AssertProducesNoError(typeof(CorrectDisableParallelForRestrictionUsageWithNativeContainer)); + } + [Test] + public void DisableParallelForRestrictionWarnsAboutArgumentType_CorrectDisableParallelForRestrictionUsageWithStruct() + { AssertProducesNoError(typeof(CorrectDisableParallelForRestrictionUsageWithStruct)); + } + [Test] + public void DisableParallelForRestrictionWarnsAboutArgumentType_IncorrectDisableParallelForRestrictionUsageWithStruct() + { AssertProducesError(typeof(IncorrectDisableParallelForRestrictionUsageWithStruct), nameof(UserError.DC0037), "structWithPrimitiveType"); + } + [Test] + public void DisableParallelForRestrictionWarnsAboutArgumentType_IncorrectDisableParallelForRestrictionUsageWithPrimitiveType() + { AssertProducesError(typeof(IncorrectDisableParallelForRestrictionUsageWithPrimitiveType), nameof(UserError.DC0037), "myVar"); } @@ -1329,10 +1380,19 @@ protected override JobHandle OnUpdate(JobHandle inputDeps) } [Test] - public void InvalidJobNamesThrow() + public void InvalidJobNamesThrow_InvalidJobNameWithSpaces() { AssertProducesError(typeof(InvalidJobNameWithSpaces), nameof(UserError.DC0043), "WithName"); + } + + [Test] + public void InvalidJobNamesThrow_InvalidJobNameStartsWithDigit() + { AssertProducesError(typeof(InvalidJobNameStartsWithDigit), nameof(UserError.DC0043), "WithName"); + } + [Test] + public void InvalidJobNamesThrow_InvalidJobNameCompilerReservedName() + { AssertProducesError(typeof(InvalidJobNameCompilerReservedName), nameof(UserError.DC0043), "WithName"); } @@ -1644,12 +1704,20 @@ protected override void OnUpdate() } [Test] - public void InvalidWithNoneComponentGeneratesError_Test() + public void InvalidWithNoneComponentGeneratesError_Test_WithNone_WithAll() { AssertProducesError(typeof(InvalidWithNoneInWithAllComponentGeneratesError_System), nameof(UserError.DC0056), nameof(LambdaJobQueryConstructionMethods.WithNone), nameof(LambdaJobQueryConstructionMethods.WithAll)); + } + [Test] + public void InvalidWithNoneComponentGeneratesError_Test_WithNone_WithAny() + { AssertProducesError(typeof(InvalidWithNoneInWithAnyComponentGeneratesError_System), nameof(UserError.DC0056), nameof(LambdaJobQueryConstructionMethods.WithNone), nameof(LambdaJobQueryConstructionMethods.WithAny)); + } + [Test] + public void InvalidWithNoneComponentGeneratesError_Test_WithNone_LambdaParameter() + { AssertProducesError(typeof(InvalidWithNoneInLambdaParamComponentGeneratesError_System), nameof(UserError.DC0056), nameof(LambdaJobQueryConstructionMethods.WithNone), "lambda parameter"); } @@ -1665,10 +1733,14 @@ public class InvalidWithNoneInLambdaParamComponentGeneratesError_System : System } [Test] - public void InvalidWithAnyComponentGeneratesError_Test() + public void InvalidWithAnyComponentGeneratesError_Test_WithAny_WithAll() { AssertProducesError(typeof(InvalidWithAnyInWithAllComponentGeneratesError_System), nameof(UserError.DC0056), nameof(LambdaJobQueryConstructionMethods.WithAny), nameof(LambdaJobQueryConstructionMethods.WithAll)); + } + [Test] + public void InvalidWithAnyComponentGeneratesError_Test_WithAny_LambdaParameter() + { AssertProducesError(typeof(InvalidWithAnyInLambdaParamComponentGeneratesError_System), nameof(UserError.DC0056), nameof(LambdaJobQueryConstructionMethods.WithAny), "lambda parameter"); } diff --git a/Unity.Entities.CodeGen/EntitiesILPostProcessor.cs b/Unity.Entities.CodeGen/EntitiesILPostProcessor.cs index 185a1365..673ade96 100644 --- a/Unity.Entities.CodeGen/EntitiesILPostProcessor.cs +++ b/Unity.Entities.CodeGen/EntitiesILPostProcessor.cs @@ -28,7 +28,11 @@ static EntitiesILPostProcessor[] FindAllEntitiesILPostProcessors() processorTypes.AddRange(assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(EntitiesILPostProcessor)) && !t.IsAbstract)); } - return processorTypes.Select(t => (EntitiesILPostProcessor)Activator.CreateInstance(t)).ToArray(); + var result = processorTypes.Select(t => (EntitiesILPostProcessor)Activator.CreateInstance(t)).ToArray(); + + Array.Sort(result); + + return result; } public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) @@ -63,6 +67,17 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) madeAnyChange |= madeChange; } + // Hack to remove Entities => Entities circular references + var selfName = assemblyDefinition.Name.FullName; + foreach (var referenceName in assemblyDefinition.MainModule.AssemblyReferences) + { + if (referenceName.FullName == selfName) + { + assemblyDefinition.MainModule.AssemblyReferences.Remove(referenceName); + break; + } + } + if (!madeAnyChange || diagnostics.Any(d => d.DiagnosticType == DiagnosticType.Error)) return new ILPostProcessResult(null, diagnostics); @@ -287,11 +302,13 @@ public override AssemblyNameReference ImportReference(AssemblyName reference) } } - abstract class EntitiesILPostProcessor + abstract class EntitiesILPostProcessor : IComparable { protected AssemblyDefinition AssemblyDefinition; public string[] Defines { get; set; } + public virtual int SortWeight => 0; + protected List _diagnosticMessages = new List(); public IEnumerable PostProcess(AssemblyDefinition assemblyDefinition, TypeDefinition[] componentSystemTypes, out bool madeAChange) @@ -333,5 +350,20 @@ public IEnumerable PostProcessUnmanaged(AssemblyDefinition as return _diagnosticMessages; } + + int IComparable.CompareTo(EntitiesILPostProcessor other) + { + // Sort the postprocessors according to weight primarily, and name secondarily + // Needed for determinism and to allow things that work on results of other postprocessors to work + // (such as job reflection data for jobs that previous post processors have just made) + int diff = SortWeight - other.SortWeight; + if (diff != 0) + return diff; + + Type ltype = GetType(); + Type rtype = other.GetType(); + return ltype.Name.CompareTo(rtype.Name); + } + } } diff --git a/Unity.Entities.CodeGen/ISystemBasePostProcessor.cs b/Unity.Entities.CodeGen/ISystemBasePostProcessor.cs index 486a3165..69e53b60 100644 --- a/Unity.Entities.CodeGen/ISystemBasePostProcessor.cs +++ b/Unity.Entities.CodeGen/ISystemBasePostProcessor.cs @@ -70,7 +70,7 @@ private TypeMemo AddStaticForwarders(TypeDefinition systemType) for (int i = 0; i < MethodNames.Length; ++i) { var name = MethodNames[i]; - var methodDef = new MethodDefinition(GeneratedMethodNames[i], MethodAttributes.Static | MethodAttributes.Private, mod.ImportReference(typeof(void))); + var methodDef = new MethodDefinition(GeneratedMethodNames[i], MethodAttributes.Static | MethodAttributes.Assembly, mod.ImportReference(typeof(void))); methodDef.Parameters.Add(new ParameterDefinition("self", ParameterAttributes.None, intPtrRef)); methodDef.Parameters.Add(new ParameterDefinition("state", ParameterAttributes.None, intPtrRef)); @@ -109,11 +109,11 @@ private void AddRegistrationCode(List memos) classDef.IsBeforeFieldInit = false; mod.Types.Add(classDef); - var funcDef = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, AssemblyDefinition.MainModule.ImportReference(typeof(void))); + var funcDef = new MethodDefinition("EarlyInit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, AssemblyDefinition.MainModule.ImportReference(typeof(void))); funcDef.Body.InitLocals = false; #if !UNITY_DOTSRUNTIME // This will need a different solution - if (!Defines.Contains("UNITY_DOTSRUNTIME") && !Defines.Contains("UNITY_DOTSPLAYER")) + if (!Defines.Contains("UNITY_DOTSRUNTIME") && !Defines.Contains("UNITY_DOTSPLAYER") && !Defines.Contains("UNITY_EDITOR")) { // Needs to run automatically in the player. var attributeCtor = AssemblyDefinition.MainModule.ImportReference(typeof(UnityEngine.RuntimeInitializeOnLoadMethodAttribute).GetConstructor(Type.EmptyTypes)); diff --git a/Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs b/Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs new file mode 100644 index 00000000..32ad3003 --- /dev/null +++ b/Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Rocks; +using Unity.CompilationPipeline.Common.Diagnostics; +using Unity.Jobs; +using Unity.Jobs.LowLevel.Unsafe; +using MethodAttributes = Mono.Cecil.MethodAttributes; +using MethodBody = Mono.Cecil.Cil.MethodBody; +using TypeAttributes = Mono.Cecil.TypeAttributes; + +namespace Unity.Entities.CodeGen +{ +#if UNITY_2020_2_OR_NEWER || UNITY_DOTSRUNTIME + class JobReflectionDataPostProcessor : EntitiesILPostProcessor + { + private static readonly string ProducerAttributeName = typeof(JobProducerTypeAttribute).FullName; + private static readonly string RegisterGenericJobTypeAttributeName = typeof(RegisterGenericJobTypeAttribute).FullName; + + // This must happen very late, after all jobs have been set up + public override int SortWeight => 0xff00; + + protected override bool PostProcessUnmanagedImpl(TypeDefinition[] unmanagedComponentSystemTypes) + { + return false; + } + + public static MethodReference AttributeConstructorReferenceFor(Type attributeType, ModuleDefinition module) + { + return module.ImportReference(attributeType.GetConstructors().Single(c => !c.GetParameters().Any())); + } + + protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) + { + var assemblyDefinition = AssemblyDefinition; + + var earlyInitHelpers = assemblyDefinition.MainModule.ImportReference(typeof(EarlyInitHelpers)).CheckedResolve(); + + var autoClassName = $"__JobReflectionRegistrationOutput__{(uint) assemblyDefinition.FullName.GetHashCode()}"; + + var classDef = new TypeDefinition("", autoClassName, TypeAttributes.Class, assemblyDefinition.MainModule.ImportReference(typeof(object))); + classDef.IsBeforeFieldInit = false; + + classDef.CustomAttributes.Add(new CustomAttribute(AttributeConstructorReferenceFor(typeof(DOTSCompilerGeneratedAttribute), assemblyDefinition.MainModule))); + + var funcDef = new MethodDefinition("CreateJobReflectionData", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, assemblyDefinition.MainModule.ImportReference(typeof(void))); + + funcDef.Body.InitLocals = false; + + classDef.Methods.Add(funcDef); + + var body = funcDef.Body; + var processor = body.GetILProcessor(); + + bool anythingChanged = false; + + var declaredGenerics = new HashSet(); + var genericJobs = new List(); + + foreach (var attr in assemblyDefinition.CustomAttributes) + { + if (attr.AttributeType.FullName != RegisterGenericJobTypeAttributeName) + continue; + + var reference = (TypeReference) attr.ConstructorArguments[0].Value; + + if (!reference.IsGenericInstance) + { + UserError.DC3001(reference); + continue; + } + + genericJobs.Add(reference); + + var def = reference.CheckedResolve(); + if (!declaredGenerics.Contains(def)) + { + declaredGenerics.Add(def); + } + } + + foreach (var t in assemblyDefinition.MainModule.Types) + { + anythingChanged |= VisitJobStructs(t, processor, body, declaredGenerics); + } + + foreach (var t in genericJobs) + { + anythingChanged |= VisitJobStructs(t, processor, body, declaredGenerics); + } + + processor.Emit(OpCodes.Ret); + + if (anythingChanged) + { + var ctorFuncDef = new MethodDefinition("EarlyInit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, assemblyDefinition.MainModule.ImportReference(typeof(void))); + +#if !UNITY_DOTSRUNTIME + if (!Defines.Contains("UNITY_DOTSPLAYER") && !Defines.Contains("UNITY_EDITOR")) + { + // Needs to run automatically in the player, but we need to + // exclude this attribute when building for the editor, or + // it will re-run the registration for every enter play mode. + var loadTypeEnumType = assemblyDefinition.MainModule.ImportReference(typeof(UnityEngine.RuntimeInitializeLoadType)); + var attributeCtor = assemblyDefinition.MainModule.ImportReference(typeof(UnityEngine.RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new[] { typeof(UnityEngine.RuntimeInitializeLoadType) })); + var attribute = new CustomAttribute(attributeCtor); + attribute.ConstructorArguments.Add(new CustomAttributeArgument(loadTypeEnumType, UnityEngine.RuntimeInitializeLoadType.AfterAssembliesLoaded)); + ctorFuncDef.CustomAttributes.Add(attribute); + } + + if (Defines.Contains("UNITY_EDITOR")) + { + // Needs to run automatically in the editor. + var attributeCtor2 = assemblyDefinition.MainModule.ImportReference(typeof(UnityEditor.InitializeOnLoadMethodAttribute).GetConstructor(Type.EmptyTypes)); + ctorFuncDef.CustomAttributes.Add(new CustomAttribute(attributeCtor2)); + } +#endif + + ctorFuncDef.Body.InitLocals = false; + + var p = ctorFuncDef.Body.GetILProcessor(); + + p.Emit(OpCodes.Ldnull); + p.Emit(OpCodes.Ldftn, funcDef); + + var delegateType = assemblyDefinition.MainModule.ImportReference(earlyInitHelpers.NestedTypes.First(x => x.Name == nameof(EarlyInitHelpers.EarlyInitFunction))); + var delegateCtor = assemblyDefinition.MainModule.ImportReference(delegateType.CheckedResolve().GetConstructors().FirstOrDefault((x) => x.Parameters.Count == 2)); + p.Emit(OpCodes.Newobj, delegateCtor); + + p.Emit(OpCodes.Call, assemblyDefinition.MainModule.ImportReference(earlyInitHelpers.Methods.First(x => x.Name == nameof(EarlyInitHelpers.AddEarlyInitFunction)))); + + p.Emit(OpCodes.Ret); + + classDef.Methods.Add(ctorFuncDef); + + assemblyDefinition.MainModule.Types.Add(classDef); + } + + return anythingChanged; + } + + private bool VisitJobStructs(TypeReference t, ILProcessor processor, MethodBody body, HashSet declaredGenerics) + { + var rt = t.CheckedResolve(); + + bool didAnything = false; + + if (rt.HasInterfaces) + { + foreach (var iface in rt.Interfaces) + { + var idef = iface.InterfaceType.CheckedResolve(); + + if (!idef.HasCustomAttributes) + continue; + + foreach (var attr in idef.CustomAttributes) + { + if (attr.AttributeType.FullName != ProducerAttributeName) + continue; + + var producerRef = (TypeReference) attr.ConstructorArguments[0].Value; + didAnything |= GenerateCalls(producerRef, t, body, processor, declaredGenerics); + } + } + } + + foreach (var nestedType in rt.NestedTypes) + { + didAnything |= VisitJobStructs(nestedType, processor, body,declaredGenerics); + } + + return didAnything; + } + + private bool GenerateCalls(TypeReference producerRef, TypeReference jobStructType, MethodBody body, ILProcessor processor, HashSet declaredGenerics) + { + try + { + var carrierType = producerRef.CheckedResolve(); + MethodDefinition methodToCall = null; + while (carrierType != null) + { + methodToCall = carrierType.GetMethods().FirstOrDefault((x) => x.Name == "EarlyJobInit" && x.Parameters.Count == 0 && x.IsStatic && x.IsPublic); + + if (methodToCall != null) + break; + + carrierType = carrierType.DeclaringType; + } + + // Legacy jobs lazy initialize. + if (methodToCall == null) + return false; + + // We need a separate solution for generic jobs + if (jobStructType.HasGenericParameters) + { + if (!declaredGenerics.Contains(jobStructType)) + AddDiagnostic(UserError.DC3002(jobStructType)); + return false; + } + + var asm = AssemblyDefinition.MainModule; + + var errorHandler = asm.ImportReference((asm.ImportReference(typeof(EarlyInitHelpers)).Resolve().Methods.First(x => x.Name == nameof(EarlyInitHelpers.JobReflectionDataCreationFailed)))); + var typeType = asm.ImportReference(typeof(Type)).CheckedResolve(); + var getTypeFromHandle = asm.ImportReference(typeType.Methods.FirstOrDefault((x) => x.Name == "GetTypeFromHandle")); + + var mref = asm.ImportReference(asm.ImportReference(methodToCall).MakeGenericInstanceMethod(jobStructType)); + + var callInsn = Instruction.Create(OpCodes.Call, mref); + var handler = Instruction.Create(OpCodes.Nop); + var landingPad = Instruction.Create(OpCodes.Nop); + + processor.Append(callInsn); + processor.Append(handler); + + // This craziness is equivalent to typeof(n) + processor.Append(Instruction.Create(OpCodes.Ldtoken, jobStructType)); + processor.Append(Instruction.Create(OpCodes.Call, getTypeFromHandle)); + processor.Append(Instruction.Create(OpCodes.Call, errorHandler)); + processor.Append(landingPad); + + var leaveSuccess = Instruction.Create(OpCodes.Leave, landingPad); + var leaveFail = Instruction.Create(OpCodes.Leave, landingPad); + processor.InsertAfter(callInsn, leaveSuccess); + processor.InsertBefore(landingPad, leaveFail); + + var exc = new ExceptionHandler(ExceptionHandlerType.Catch); + exc.TryStart = callInsn; + exc.TryEnd = leaveSuccess.Next; + exc.HandlerStart = handler; + exc.HandlerEnd = leaveFail.Next; + exc.CatchType = asm.ImportReference(typeof(Exception)); + body.ExceptionHandlers.Add(exc); + return true; + } + catch (Exception ex) + { + AddDiagnostic(InternalCompilerError.DCICE300(producerRef, jobStructType, ex)); + } + + return false; + } + } +#endif +} + diff --git a/Unity.Entities.Tests/BurstCompatibilityTests.gen.cs.meta b/Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs.meta similarity index 83% rename from Unity.Entities.Tests/BurstCompatibilityTests.gen.cs.meta rename to Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs.meta index b01c2988..5d5d1ace 100644 --- a/Unity.Entities.Tests/BurstCompatibilityTests.gen.cs.meta +++ b/Unity.Entities.CodeGen/JobReflectionDataPostProcessor.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8907ae1b9747c9a4d974ff6b3560f0e8 +guid: 040d66501dd7d3f4bac213e5e8b3179e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Unity.Entities.CodeGen/LambdaJobs/UserError.cs b/Unity.Entities.CodeGen/LambdaJobs/UserError.cs index e4561a08..4089c082 100644 --- a/Unity.Entities.CodeGen/LambdaJobs/UserError.cs +++ b/Unity.Entities.CodeGen/LambdaJobs/UserError.cs @@ -60,6 +60,11 @@ public static DiagnosticMessage DCICE010(MethodDefinition method, Instruction in { return UserError.MakeError(nameof(DCICE010), $"Ldftn opcode was not preceeded with Ldloc opcode while IL was being re-written to keep DisplayClass on the stack for method {method.Name}.", method, instruction); } + + public static DiagnosticMessage DCICE300(TypeReference producerReference, TypeReference jobStructType, Exception ex) + { + return UserError.MakeError(nameof(DCICE010), $"Unexpected error while generating automatic registration for job provider {producerReference.FullName} via job struct {jobStructType.FullName}. Please report this error.\nException: {ex.Message}", method: null, instruction: null); + } } static class UserError @@ -385,6 +390,16 @@ public static DiagnosticMessage DC0057(MethodDefinition containingMethod, Instru return MakeError(nameof(DC0057), $"{nameof(LambdaJobDescriptionConstructionMethods.WithStructuralChanges)} cannot be used with name Job.WithCode. {nameof(LambdaJobDescriptionConstructionMethods.WithStructuralChanges)} should instead be used with Entities.ForEach.", containingMethod, instruction); } + public static DiagnosticMessage DC3001(TypeReference type) + { + return MakeError(nameof(DC3001), $"{type.FullName}: [RegisterGenericJobType] requires an instance of a generic type", method: null, instruction: null); + } + + public static DiagnosticMessage DC3002(TypeReference jobStructType) + { + return MakeError(nameof(DC3002), $"{jobStructType.FullName}: generic jobs cannot have their reflection data auto-registered - you must use the assembly-level RegisterGenericJobType attribute to specify which instantiations you need", method: null, instruction: null); + } + static DiagnosticMessage MakeInternal(DiagnosticType type, string errorCode, string messageData, MethodDefinition method, Instruction instruction) { var result = new DiagnosticMessage {Column = 0, Line = 0, DiagnosticType = type, File = ""}; diff --git a/Unity.Entities.CodeGen/SingletonAccess/SingletonAccessPostProcessor.cs b/Unity.Entities.CodeGen/SingletonAccess/SingletonAccessPostProcessor.cs index ee0f7da9..b64649f2 100644 --- a/Unity.Entities.CodeGen/SingletonAccess/SingletonAccessPostProcessor.cs +++ b/Unity.Entities.CodeGen/SingletonAccess/SingletonAccessPostProcessor.cs @@ -88,16 +88,20 @@ internal static bool Rewrite(MethodDefinition containingMethod, { if (instruction.IsInvocation(out var methodReference)) { - // Ensure a good match with the singleton method we want to patch - if (methodReference.DeclaringType.TypeReferenceEquals(typeof(ComponentSystemBase)) && - SingletonAccessMethodDescriptions.TryGetValue(methodReference.Name, out var methodDescription) && - methodReference is GenericInstanceMethod genericInvocation && genericInvocation.GenericArguments.Count() == 1 && - methodReference.Parameters.Count() == methodDescription.parameterCount) + if (methodReference.DeclaringType.TypeReferenceEquals(typeof(ComponentSystemBase))) { - if (!madeChange) - containingMethod.Body.SimplifyMacros(); - PatchSingletonMethod(containingMethod, methodReference, instruction, entityQueryFields, methodDescription); - madeChange = true; + // Ensure a good match with the singleton method we want to patch + // and that the generic argument is not a generic parameter itself (in which case it is difficult to predetermine query type) + if (SingletonAccessMethodDescriptions.TryGetValue(methodReference.Name, out var methodDescription) && + methodReference.Parameters.Count() == methodDescription.parameterCount && + methodReference is GenericInstanceMethod genericInstanceMethod && genericInstanceMethod.GenericArguments.Count() == 1 && + !genericInstanceMethod.GenericArguments.First().IsGenericParameter) + { + if (!madeChange) + containingMethod.Body.SimplifyMacros(); + PatchSingletonMethod(containingMethod, methodReference, instruction, entityQueryFields, methodDescription); + madeChange = true; + } } } } diff --git a/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs b/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs index 3527739e..874bc277 100644 --- a/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs +++ b/Unity.Entities.CodeGen/StaticTypeRegistry/StaticTypeRegistryPostProcessor.cs @@ -74,6 +74,7 @@ internal partial class StaticTypeRegistryPostProcessor : EntitiesILPostProcessor MethodDefinition GeneratedRegistryCCTORDef; bool IsReleaseConfig; bool IsMono; + bool IsNetDots; int ArchBits; /// @@ -84,6 +85,12 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) { bool madeChange = false; + IsReleaseConfig = !EntitiesILPostProcessors.Defines.Contains("DEBUG"); + IsMono = EntitiesILPostProcessors.Defines.Contains("UNITY_DOTSRUNTIME_DOTNET") && (EntitiesILPostProcessors.Defines.Contains("UNITY_MACOSX") || EntitiesILPostProcessors.Defines.Contains("UNITY_LINUX")); + IsNetDots = EntitiesILPostProcessors.Defines.Contains("NET_DOTS"); + ArchBits = EntitiesILPostProcessors.Defines.Contains("UNITY_DOTSRUNTIME64") ? 64 : 32; + + (var typeGenInfoList, var systemList) = GatherTypeInformation(); if (typeGenInfoList.Count > 0 || systemList.Count > 0) { @@ -131,10 +138,6 @@ void InitializeReferences() typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) })); - - IsReleaseConfig = !EntitiesILPostProcessors.Defines.Contains("DEBUG"); - IsMono = EntitiesILPostProcessors.Defines.Contains("UNITY_DOTSRUNTIME_DOTNET") && (EntitiesILPostProcessors.Defines.Contains("UNITY_MACOSX") || EntitiesILPostProcessors.Defines.Contains("UNITY_LINUX")); - ArchBits = EntitiesILPostProcessors.Defines.Contains("UNITY_DOTSRUNTIME64") ? 64 : 32; } TypeCategory FindTypeCategoryForType(TypeDefinition typeDef) @@ -189,7 +192,9 @@ TypeCategory FindTypeCategoryForTypeRecursive(TypeDefinition typeDef) if (!type.HasGenericParameters) { TypeCategory typeCategory; - if (type.IsClass) + + // Managed Components are not supported in NET_DOTS + if (type.IsClass && !IsNetDots) typeCategory = FindTypeCategoryForTypeRecursive(type); else typeCategory = FindTypeCategoryForType(type); diff --git a/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs b/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs index e8326191..eea301e6 100644 --- a/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs +++ b/Unity.Entities.CodeGen/StaticTypeRegistry/TypeInfoGeneration.cs @@ -819,6 +819,14 @@ internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategor blobAssetRefOffsets = TypeUtils.GetFieldOffsetsOf("Unity.Entities.BlobAssetReference`1", typeRef, ArchBits); alignAndSize = TypeUtils.AlignAndSizeOfType(typeRef, ArchBits); } + else if (isManaged && IsNetDots + // Todo: ISharedComponents are currently commonly managed as this was the only mechanism for storing managed + // data in ECS. Until unmanaged sharedcomponents are available we allow managed shared components in NET_DOTS + // https://unity3d.atlassian.net/browse/DOTSR-1865 + && typeCategory != TypeCategory.ISharedComponentData) + { + throw new ArgumentException($"Found a managed component '{typeRef.FullName}'. Managed components are not supported when building for the Tiny configuration. Change the type to be a struct or build with the NetStandard 2.0 configuration."); + } int typeIndex = m_TotalTypeCount++; bool isSystemStateBufferElement = typeDef.Interfaces.Select(i => i.InterfaceType.Name).Contains(nameof(ISystemStateBufferElementData)); @@ -895,6 +903,9 @@ internal unsafe TypeGenInfo CreateTypeGenInfo(TypeReference typeRef, TypeCategor if (typeCategory == TypeCategory.BufferData) { + if (elementSize == 0) + throw new ArgumentException($"Component '{typeRef.FullName}' is used as a buffer data, but has size == 0."); + // If we haven't overridden the bufferSize via an attribute if (bufferCapacity == -1) { diff --git a/Unity.Entities.CodeGen/TestCaseILPP.cs b/Unity.Entities.CodeGen/TestCaseILPP.cs index 48e11718..a5614814 100644 --- a/Unity.Entities.CodeGen/TestCaseILPP.cs +++ b/Unity.Entities.CodeGen/TestCaseILPP.cs @@ -123,7 +123,8 @@ bool HasCustomAttribute(MethodDefinition m, string attributeName) } // already checked the base; can start one up the chain. - var parent = attr.AttributeType.Resolve().BaseType; + var attributeTypeDef = attr.AttributeType.Resolve(); + var parent = attributeTypeDef?.BaseType; while (parent != null) { if (parent.FullName == attributeName || parent.FullName == fullAttrName) diff --git a/Unity.Entities.Editor.Tests/EntityDebuggerTests.cs b/Unity.Entities.Editor.Tests/EntityDebuggerTests.cs index 13f7e024..557d7c80 100644 --- a/Unity.Entities.Editor.Tests/EntityDebuggerTests.cs +++ b/Unity.Entities.Editor.Tests/EntityDebuggerTests.cs @@ -37,7 +37,6 @@ private static void CloseAllDebuggers() private const string World2Name = "Test World 2"; private World World2; - private PlayerLoopSystem m_PrevPlayerLoop; public override void Setup() { @@ -50,9 +49,7 @@ public override void Setup() m_System = World.GetOrCreateSystem(); World.GetOrCreateSystem().AddSystemToUpdateList(m_System); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); // TODO(DOTS-2283): shouldn't stomp default player loop here - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(World, ref playerLoop); - PlayerLoop.SetPlayerLoop(playerLoop); + ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(World); World2 = new World(World2Name); var emptySys = World2.GetOrCreateSystem(); diff --git a/Unity.Entities.Editor.Tests/ListViewTests.cs b/Unity.Entities.Editor.Tests/ListViewTests.cs index 4f76ede0..f0cafca2 100644 --- a/Unity.Entities.Editor.Tests/ListViewTests.cs +++ b/Unity.Entities.Editor.Tests/ListViewTests.cs @@ -40,9 +40,7 @@ public override void Setup() simGroup.AddSystemToUpdateList(emptySys); simGroup.SortSystems(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); // TODO(DOTS-2283): shouldn't stomp default player loop here - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(World.DefaultGameObjectInjectionWorld, ref playerLoop); - PlayerLoop.SetPlayerLoop(playerLoop); + ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(World.DefaultGameObjectInjectionWorld); } public override void TearDown() diff --git a/Unity.Entities.Editor/EntityDebugger/EntityDebugger.cs b/Unity.Entities.Editor/EntityDebugger/EntityDebugger.cs index bc6ef921..fad8626a 100644 --- a/Unity.Entities.Editor/EntityDebugger/EntityDebugger.cs +++ b/Unity.Entities.Editor/EntityDebugger/EntityDebugger.cs @@ -443,6 +443,25 @@ public static string FormatBytesForDisplay(long bytes) return String.Format("{0:0.00} GB", bytes / 1024.0); } + public static string FormatBytesForDisplay(ulong bytes) + { + if (bytes < 0) + return "Unknown"; + + if (bytes < 512) + return String.Format("{0} B", bytes); + + if (bytes < 512 * 1024) + return String.Format("{0:0.0} KB", bytes / 1024.0); + + bytes /= 1024; + if (bytes < 512 * 1024) + return String.Format("{0:0.0} MB", bytes / 1024.0); + + bytes /= 1024; + return String.Format("{0:0.00} GB", bytes / 1024.0); + } + private void EntityHeader() { GUILayout.BeginHorizontal(Styles.ToolbarStyle); @@ -466,8 +485,8 @@ private void EntityHeader() long pageSize; long chunkReservedPages; long chunkCommittedPages; - long chunkReservedBytes; - long chunkCommittedBytes; + ulong chunkReservedBytes; + ulong chunkCommittedBytes; EntityComponentStore.GetChunkMemoryStats(out chunkReservedPages, out chunkCommittedPages, out chunkReservedBytes, out chunkCommittedBytes, out pageSize); if(chunkReservedPages > 0) { @@ -475,7 +494,7 @@ private void EntityHeader() $"Committed: {chunkCommittedPages} pages / " + $"{FormatBytesForDisplay(chunkCommittedBytes)}, " + $"In use by selected query: {entityListView.ChunkArray.Length * 16384 / pageSize } pages / " + - $"{FormatBytesForDisplay(entityListView.ChunkArray.Length * 16384)}", Styles.LabelStyle); + $"{FormatBytesForDisplay((ulong)entityListView.ChunkArray.Length * 16384UL)}", Styles.LabelStyle); } ShowingChunkInfoView = GUILayout.Toggle(ShowingChunkInfoView, "Chunk Info", Styles.ToolbarButtonStyle); GUILayout.EndHorizontal(); diff --git a/Unity.Entities.Editor/Unity.Entities.Editor.asmdef b/Unity.Entities.Editor/Unity.Entities.Editor.asmdef index 993289d7..6f32299e 100644 --- a/Unity.Entities.Editor/Unity.Entities.Editor.asmdef +++ b/Unity.Entities.Editor/Unity.Entities.Editor.asmdef @@ -11,6 +11,7 @@ "Unity.Scenes", "Unity.Serialization", "Unity.Build", + "Unity.Build.Internals", "Unity.Build.Classic", "UnityEditor.UI", "Unity.Build.Classic.Private" diff --git a/Unity.Entities.Hybrid.Tests/Conversion/ConversionJournalDataTests.cs b/Unity.Entities.Hybrid.Tests/Conversion/ConversionJournalDataTests.cs index 62caee5f..e34f4937 100644 --- a/Unity.Entities.Hybrid.Tests/Conversion/ConversionJournalDataTests.cs +++ b/Unity.Entities.Hybrid.Tests/Conversion/ConversionJournalDataTests.cs @@ -177,14 +177,6 @@ public void RecordPrimaryEntity_WithExistingPrimaryEntity_Throws() Assert.DoesNotThrow(() => m_JournalData.RecordPrimaryEntity(2, new Entity { Index = 1, Version = 2 })); } - [Test] - public void RecordAdditionalEntity_WithNoPrimaryEntity_Throws() - { - Assert.Throws(() => m_JournalData.RecordAdditionalEntityAt(1, new Entity { Index = 1, Version = 2 })); - m_JournalData.RecordPrimaryEntity(1, new Entity { Index = 1, Version = 2 }); - Assert.DoesNotThrow(() => m_JournalData.RecordAdditionalEntityAt(1, new Entity { Index = 3, Version = 4 })); - } - [Test] public void Entities_StoredInSerialOrder() { @@ -258,5 +250,86 @@ public void Events_StoredInCorrectOrder() (instanceId, new LogEventData { Message = "extra 2" }) })); } + + [Test] + public void RemovePrimaryEntity_RemovesExistingPrimaryEntity() + { + var e = new Entity {Index = 1, Version = 1}; + m_JournalData.RecordPrimaryEntity(1, e); + Assert.AreEqual(e, m_JournalData.RemovePrimaryEntity(1)); + Assert.IsFalse(m_JournalData.TryGetPrimaryEntity(1, out _)); + } + + [Test] + public void RemovePrimaryEntity_WithInvalidEntity_ReturnsNull() + { + Assert.AreEqual(Entity.Null, m_JournalData.RemovePrimaryEntity(1)); + } + + [Test] + public void RemovePrimaryEntity_RemovesAdditionalEntities() + { + var go = CreateGameObject(); + var instanceId = go.GetInstanceID(); + var e0 = new Entity {Index = 1, Version = 1}; + var e1 = new Entity {Index = 3, Version = 17}; + m_JournalData.RecordPrimaryEntity(instanceId, e0); + var s0 = m_JournalData.ReserveAdditionalEntity(instanceId); + m_JournalData.RecordAdditionalEntityAt(s0.id, e1); + Assert.That(m_JournalData.SelectEntities(go), Is.EqualTo(new[] { e0, e1 })); + + m_JournalData.RemovePrimaryEntity(instanceId); + m_JournalData.RecordPrimaryEntity(instanceId, e0); + Assert.That(m_JournalData.SelectEntities(go), Is.EqualTo(new[] { e0 })); + } + + [Test] + public void RecordPrimaryEntity_AddsNewEntry() + { + var e = new Entity {Index = 1, Version = 1}; + m_JournalData.RecordPrimaryEntity(1, e); + Assert.IsTrue(m_JournalData.TryGetPrimaryEntity(1, out var testEntity)); + Assert.AreEqual(e, testEntity); + } + + [Test] + public void RecordPrimaryEntity_AfterRemoval_AddsNewEntry() + { + var e = new Entity {Index = 1, Version = 1}; + m_JournalData.RecordPrimaryEntity(1, e); + m_JournalData.RemovePrimaryEntity(1); + var e2 = new Entity {Index = 3, Version = 17}; + m_JournalData.RecordPrimaryEntity(1, e2); + Assert.IsTrue(m_JournalData.TryGetPrimaryEntity(1, out var testEntity)); + Assert.AreEqual(e2, testEntity); + } + + [Test] + public void RecordingAndRemovingPrimaryEntitiesRepeatedly_DoesNotThrow() + { + const int n = 15; + var es = new Entity[n]; + for (int i = 0; i < es.Length; i++) + { + es[i] = new Entity {Index = i + 1, Version = 17}; + m_JournalData.RecordPrimaryEntity(i, es[i]); + Assert.IsTrue(m_JournalData.TryGetPrimaryEntity(i, out var e)); + Assert.AreEqual(es[i], e); + } + + for (int i = 0; i < es.Length; i += 2) + { + var e = m_JournalData.RemovePrimaryEntity(i); + Assert.AreEqual(es[i], e); + Assert.IsFalse(m_JournalData.TryGetPrimaryEntity(i, out _)); + } + + for (int i = 0; i < es.Length; i += 2) + { + m_JournalData.RecordPrimaryEntity(i, es[i]); + Assert.IsTrue(m_JournalData.TryGetPrimaryEntity(i, out var e)); + Assert.AreEqual(es[i], e); + } + } } } diff --git a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs b/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs index 28ae86f3..bde87d39 100644 --- a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs +++ b/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs @@ -7,7 +7,7 @@ namespace Unity.Entities.Tests.Conversion { class MultiListTests { - MultiList m_MultiList; + MultiList> m_MultiList; [SetUp] public void SetUp() @@ -21,16 +21,18 @@ public void SetUp() public void TearDown() { MultiListDebugUtility.ValidateIntegrity(ref m_MultiList); + m_MultiList.Dispose(); } [Test] public void AddHead_AfterRelease_ReusesCapacity() { + m_MultiList.Dispose(); m_MultiList.Init(); m_MultiList.EnsureCapacity(2); m_MultiList.SetHeadIdsCapacity(2); - Assert.That(m_MultiList.Data.Length, Is.EqualTo(2)); + Assert.That(m_MultiList.Data.Data.Length, Is.GreaterThanOrEqualTo(2)); var oldHeadIds = m_MultiList.HeadIds; var oldNext = m_MultiList.Next; var oldData = m_MultiList.Data; @@ -40,15 +42,15 @@ public void AddHead_AfterRelease_ReusesCapacity() m_MultiList.ReleaseList(1); - Assert.That(m_MultiList.HeadIds, Is.SameAs(oldHeadIds)); - Assert.That(m_MultiList.Next, Is.SameAs(oldNext)); - Assert.That(m_MultiList.Data, Is.SameAs(oldData)); + Assert.That(m_MultiList.HeadIds, Is.EqualTo(oldHeadIds)); + Assert.That(m_MultiList.Next, Is.EqualTo(oldNext)); + Assert.That(m_MultiList.Data, Is.EqualTo(oldData)); m_MultiList.AddHead(1, "2"); - Assert.That(m_MultiList.HeadIds, Is.SameAs(oldHeadIds)); - Assert.That(m_MultiList.Next, Is.SameAs(oldNext)); - Assert.That(m_MultiList.Data, Is.SameAs(oldData)); + Assert.That(m_MultiList.HeadIds, Is.EqualTo(oldHeadIds)); + Assert.That(m_MultiList.Next, Is.EqualTo(oldNext)); + Assert.That(m_MultiList.Data, Is.EqualTo(oldData)); } [Test] @@ -64,28 +66,29 @@ public void AddHead_WithReusedHeadId_Throws() [Test] public void AddVarious_WithInvalidId_Throws() { - var(min, max) = (-1, m_MultiList.HeadIds.Length); + const int invalid = -1; + var outOfRange = m_MultiList.HeadIds.Length; - Assert.Throws(() => m_MultiList.AddHead(min, "0")); - Assert.Throws(() => m_MultiList.AddHead(max, "0")); + Assert.Throws(() => m_MultiList.AddHead(invalid, "0")); + Assert.Throws(() => m_MultiList.AddHead(outOfRange, "0")); - Assert.Throws(() => m_MultiList.Add(min, "0")); - Assert.Throws(() => m_MultiList.Add(max, "0")); + Assert.Throws(() => m_MultiList.Add(invalid, "0")); + Assert.Throws(() => m_MultiList.Add(outOfRange, "0")); - Assert.Throws(() => m_MultiList.AddTail(min)); - Assert.Throws(() => m_MultiList.AddTail(max)); + Assert.Throws(() => m_MultiList.AddTail(invalid)); + Assert.Throws(() => m_MultiList.AddTail(outOfRange)); - Assert.Throws(() => m_MultiList.AddTail(min, "0")); - Assert.Throws(() => m_MultiList.AddTail(max, "0")); + Assert.Throws(() => m_MultiList.AddTail(invalid, "0")); + Assert.Throws(() => m_MultiList.AddTail(outOfRange, "0")); - Assert.Throws(() => m_MultiList.ReleaseList(min)); - Assert.Throws(() => m_MultiList.ReleaseList(max)); + Assert.Throws(() => m_MultiList.ReleaseList(invalid)); + Assert.Throws(() => m_MultiList.ReleaseList(outOfRange)); - Assert.Throws(() => m_MultiList.ReleaseListKeepHead(min)); - Assert.Throws(() => m_MultiList.ReleaseListKeepHead(max)); + Assert.Throws(() => m_MultiList.ReleaseListKeepHead(invalid)); + Assert.Throws(() => m_MultiList.ReleaseListKeepHead(outOfRange)); - Assert.DoesNotThrow(() => { foreach (var _ in m_MultiList.SelectListAt(min)) {} }); - Assert.Throws(() => { foreach (var _ in m_MultiList.SelectListAt(max)) {} }); + Assert.Throws(() => m_MultiList.SelectList(invalid)); + Assert.Throws(() => m_MultiList.SelectList(outOfRange)); } [Test] @@ -110,7 +113,7 @@ public unsafe void AddTailMultiple_WithSingle_AddsSingle() m_MultiList.AddHead(0, "0a"); int id; m_MultiList.AddTail(0, &id, 1); - m_MultiList.Data[id] = "0b"; + m_MultiList.Data.Data[id] = "0b"; MultiListDebugUtility.ValidateIntegrity(ref m_MultiList); var data = MultiListDebugUtility.SelectAllData(m_MultiList); Assert.That(data, Is.EqualTo(new[] {new[] {"0a", "0b"}})); @@ -122,11 +125,11 @@ public unsafe void AddTailMultiple_WithMultiple_AddsMultiple() m_MultiList.AddHead(0, "0a"); int* ids = stackalloc int[5]; m_MultiList.AddTail(0, ids, 5); - m_MultiList.Data[ids[0]] = "0b"; - m_MultiList.Data[ids[1]] = "0c"; - m_MultiList.Data[ids[2]] = "0d"; - m_MultiList.Data[ids[3]] = "0e"; - m_MultiList.Data[ids[4]] = "0f"; + m_MultiList.Data.Data[ids[0]] = "0b"; + m_MultiList.Data.Data[ids[1]] = "0c"; + m_MultiList.Data.Data[ids[2]] = "0d"; + m_MultiList.Data.Data[ids[3]] = "0e"; + m_MultiList.Data.Data[ids[4]] = "0f"; MultiListDebugUtility.ValidateIntegrity(ref m_MultiList); var data = MultiListDebugUtility.SelectAllData(m_MultiList); Assert.That(data, Is.EqualTo(new[] {new[] {"0a", "0b", "0c", "0d", "0e", "0f"}})); @@ -203,7 +206,7 @@ public void AddTail_WithDeferredDataSet_Matches() Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( new[] { new[] { "0", "0a", null, "0c" } })); - m_MultiList.Data[added.id] = "0b"; + m_MultiList.Data.Data[added.id] = "0b"; Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( new[] { new[] { "0", "0a", "0b", "0c" } })); @@ -277,7 +280,7 @@ public void EnumeratorMoveNext_WithDefault_ReturnsEmpty() Assert.That(e.MoveNext(), Is.False); } - using (var e = MultiListEnumerator.Empty) + using (var e = MultiListEnumerator>.Empty) { Assert.That(e.MoveNext(), Is.False); } @@ -317,7 +320,7 @@ public void EnumeratorCount() [Test] public void EnumeratorCurrent_WithDefault_Throws() { - using (var e = MultiListEnumerator.Empty) + using (var e = MultiListEnumerator>.Empty) { // ReSharper disable once NotAccessedVariable string s; diff --git a/Unity.Entities.Hybrid.Tests/WorldScriptUpdateOrderTests.cs b/Unity.Entities.Hybrid.Tests/WorldScriptUpdateOrderTests.cs index ec10138f..1cc7aba8 100644 --- a/Unity.Entities.Hybrid.Tests/WorldScriptUpdateOrderTests.cs +++ b/Unity.Entities.Hybrid.Tests/WorldScriptUpdateOrderTests.cs @@ -1,7 +1,8 @@ +using System; using NUnit.Framework; using Unity.Entities.Hybrid.Tests; using UnityEngine.LowLevel; -using UnityEngine.TestTools; +using UnityEngine.PlayerLoop; namespace Unity.Entities.Tests { @@ -14,6 +15,7 @@ public class WorldScriptUpdateOrderTests public void Setup() { m_PrevPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); m_DefaultWorld.Setup(); } @@ -31,15 +33,192 @@ public void AddRemoveScriptUpdate() ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(newWorld); Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(newWorld)); - PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); // TODO(DOTS-2283): Shouldn't stomp default player loop here + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(newWorld)); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); // TODO(DOTS-2283): Shouldn't stomp default player loop here + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(World.DefaultGameObjectInjectionWorld, ref playerLoop); PlayerLoop.SetPlayerLoop(playerLoop); Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(World.DefaultGameObjectInjectionWorld)); } + [Test] + public void IsInPlayerLoop_WorldNotInPlayerLoop_ReturnsFalse() + { + using (var world = new World("Test World")) + { + world.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); + } + } + + [Test] + public void IsInPlayerLoop_WorldInPlayerLoop_ReturnsTrue() + { + using (var world = new World("Test World")) + { + world.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref playerLoop); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); + } + } + + [Test] + public void RemoveFromPlayerLoop_WorldNotInPlayerLoop_DoesntThrow() + { + using (var world = new World("Test World")) + { + world.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(world, ref playerLoop); + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); + } + } + + [Test] + public void RemoveFromPlayerLoop_WorldInPlayerLoop_Works() + { + using (var world = new World("Test World")) + { + world.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref playerLoop); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); + ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(world, ref playerLoop); + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); + } + } + + [Test] + public void AddToPlayerLoop_AddTwoWorlds_BothAreAdded() + { + using (var worldA = new World("Test World A")) + using (var worldB = new World("Test World B")) + { + worldA.CreateSystem(); + worldB.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldA, ref playerLoop); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldB, ref playerLoop); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); + } + } + + [Test] + public void RemoveFromPlayerLoop_OtherWorldsInPlayerLoop_NotAffected() + { + using (var worldA = new World("Test World A")) + using (var worldB = new World("Test World B")) + { + worldA.CreateSystem(); + worldB.CreateSystem(); + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldA, ref playerLoop); + ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldB, ref playerLoop); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); + + ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(worldA, ref playerLoop); + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); + } + } + + class InvalidPlayerLoopSystemType + { + } + + class TestSystem : ComponentSystemBase + { + public override void Update() + { + throw new System.NotImplementedException(); + } + } + + [Test] + public void AppendSystemToPlayerLoopList_InvalidPlayerLoopSystemType_Throws() + { + using (var world = new World("Test World")) + { + var sys = world.CreateSystem(); + var playerLoop = PlayerLoop.GetCurrentPlayerLoop(); + Assert.That( + () => ScriptBehaviourUpdateOrder.AppendSystemToPlayerLoopList(sys, ref playerLoop, + typeof(InvalidPlayerLoopSystemType)), + Throws.ArgumentException.With.Message.Matches( + @"Could not find PlayerLoopSystem with type=.+InvalidPlayerLoopSystemType")); + } + } + + bool IsSystemInSubsystemList(PlayerLoopSystem[] subsystemList, ComponentSystemBase system) + { + if (subsystemList == null) + return false; + for (int i = 0; i < subsystemList.Length; ++i) + { + var pls = subsystemList[i]; + if (typeof(ComponentSystemBase).IsAssignableFrom(pls.type)) + { + var wrapper = pls.updateDelegate.Target as ScriptBehaviourUpdateOrder.DummyDelegateWrapper; + if (wrapper.System == system) + { + return true; + } + } + } + return false; + } + + void ValidatePostAppendPlayerLoop(PlayerLoopSystem playerLoop, Type targetStageType, ComponentSystemBase system) + { + if (playerLoop.type == targetStageType) + Assert.IsTrue(IsSystemInSubsystemList(playerLoop.subSystemList, system)); + else + Assert.IsFalse(IsSystemInSubsystemList(playerLoop.subSystemList, system)); + + if (playerLoop.subSystemList != null) + { + for (int i = 0; i < playerLoop.subSystemList.Length; ++i) + { + ValidatePostAppendPlayerLoop(playerLoop.subSystemList[i], targetStageType, system); + } + } + } + + [Test] + public void AppendSystemToPlayerLoopList_AddToNestedList_Works() + { + using (var world = new World("Test World")) + { + var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); + var sys = world.CreateSystem(); + Type targetStageType = typeof(PreLateUpdate.LegacyAnimationUpdate); + ScriptBehaviourUpdateOrder.AppendSystemToPlayerLoopList(sys, ref playerLoop, targetStageType); + ValidatePostAppendPlayerLoop(playerLoop, targetStageType, sys); + } + } + + [Test] + public void CurrentPlayerLoopWrappers_Work() + { + using (var world = new World("Test World")) + { + // world must have at least one of the default top-level groups to add + var initSysGroup = world.CreateSystem(); + + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(world)); + ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(world); + Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(world)); + ScriptBehaviourUpdateOrder.RemoveWorldFromCurrentPlayerLoop(world); + Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInCurrentPlayerLoop(world)); + } + } + [TearDown] public void TearDown() { diff --git a/Unity.Entities.Hybrid/AssemblyInfo.cs b/Unity.Entities.Hybrid/AssemblyInfo.cs index c00b894d..bd03e9b3 100644 --- a/Unity.Entities.Hybrid/AssemblyInfo.cs +++ b/Unity.Entities.Hybrid/AssemblyInfo.cs @@ -17,3 +17,4 @@ [assembly: InternalsVisibleTo("Unity.Entities.Hybrid.CodeGen")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid.CodeGen.Tests")] [assembly: InternalsVisibleTo("Unity.Tiny.Rendering.Authoring")] +[assembly: InternalsVisibleTo("Unity.Entities.Runtime.Build")] diff --git a/Unity.Entities.Hybrid/GameObjectConversion/ConversionJournalData.cs b/Unity.Entities.Hybrid/GameObjectConversion/ConversionJournalData.cs index 77d47f58..c8beaafb 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/ConversionJournalData.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/ConversionJournalData.cs @@ -15,31 +15,60 @@ struct LogEventData : IConversionEventData public string Message; } + internal struct ConvertedEntitiesAccessor + { + NativeHashMap m_HeadIdIndices; // object instanceId -> front index + MultiList> m_Entities; + + internal ConvertedEntitiesAccessor(NativeHashMap headIndices, + MultiList> entities) + { + m_HeadIdIndices = headIndices; + m_Entities = entities; + } + + + internal MultiListEnumerator GetEntities(int instanceId) + { + if (!m_HeadIdIndices.TryGetValue(instanceId, out var headIdIndex)) + return MultiListEnumerator.Empty; + return new MultiListEnumerator(m_Entities.SelectList(headIdIndex)); + } + } + partial struct ConversionJournalData : IDisposable { NativeHashMap m_HeadIdIndices; // object instanceId -> front index - int m_HeadIdIndicesCount; + NativeList m_FreeHeadIds; + private int m_HeadIdCount; + // Only for UnityEngine component types to be stored in a companion GameObject - Dictionary m_HybridHeadIdIndices; // maps to MultiList m_HybridTypes + // maps GameObject to MultiList m_HybridTypes + // for 2020.2, this could be based on instance IDs instead + Dictionary m_HybridHeadIdIndices; + internal Dictionary HybridHeadIdIndices => m_HybridHeadIdIndices; - public Dictionary HybridHeadIdIndices { get => m_HybridHeadIdIndices; } public void Dispose() { m_HeadIdIndices.Dispose(); + m_FreeHeadIds.Dispose(); + m_Entities.Dispose(); + m_LogEvents.Dispose(); + m_HybridTypes.Dispose(); } // ** keep this block in sync ** (begin) - MultiList m_Entities; - MultiList m_LogEvents; - MultiList m_HybridTypes; + MultiList> m_Entities; + MultiList> m_LogEvents; + MultiList> m_HybridTypes; public void Init() { m_HeadIdIndices = new NativeHashMap(1000, Allocator.Persistent); - m_HeadIdIndicesCount = 0; + m_FreeHeadIds = new NativeList(Allocator.Persistent); m_HybridHeadIdIndices = new Dictionary(); m_Entities.Init(); @@ -47,15 +76,20 @@ public void Init() m_HybridTypes.Init(); } - public void RemoveForIncremental(GameObject gameObject) + internal ConvertedEntitiesAccessor GetConvertedEntitiesAccessor() + { + return new ConvertedEntitiesAccessor(m_HeadIdIndices, m_Entities); + } + + public void RemoveForIncremental(int instanceId, GameObject go) { - if (m_HeadIdIndices.TryGetValue(gameObject.GetInstanceID(), out var headIdIndex)) + if (m_HeadIdIndices.TryGetValue(instanceId, out var headIdIndex)) { m_Entities.ReleaseListKeepHead(headIdIndex); m_LogEvents.ReleaseList(headIdIndex); } - if (m_HybridHeadIdIndices.TryGetValue(gameObject, out headIdIndex)) + if (go != null && m_HybridHeadIdIndices.TryGetValue(go, out headIdIndex)) { m_HybridTypes.ReleaseList(headIdIndex); } @@ -74,7 +108,14 @@ int GetOrAddHeadIdIndex(int objectInstanceId) { if (!m_HeadIdIndices.TryGetValue(objectInstanceId, out var headIdIndex)) { - headIdIndex = m_HeadIdIndicesCount++; + if (!m_FreeHeadIds.IsEmpty) + { + int end = m_FreeHeadIds.Length - 1; + headIdIndex = m_FreeHeadIds[end]; + m_FreeHeadIds.Length -= 1; + } + else + headIdIndex = m_HeadIdCount++;; m_HeadIdIndices.Add(objectInstanceId, headIdIndex); var headIdsCapacity = headIdIndex + 1; @@ -106,26 +147,26 @@ int GetOrAddHybridHeadIdIndex(GameObject gameObject) } // creates new head, returns false if already had one - void AddHead(int objectInstanceId, ref MultiList store, in T data) => + void AddHead(int objectInstanceId, ref MultiList store, in T data) where I : IMultiListDataImpl => store.AddHead(GetOrAddHeadIdIndex(objectInstanceId), data); // creates new head or adds a new entry - void Add(int objectInstanceId, ref MultiList store, in T data) => + void Add(int objectInstanceId, ref MultiList store, in T data) where I : IMultiListDataImpl => store.Add(GetOrAddHeadIdIndex(objectInstanceId), data); // requires existing sublist, walks to end and adds, returns count (can be slow with large count) - (int id, int serial) AddTail(int objectInstanceId, ref MultiList store) => + (int id, int serial) AddTail(int objectInstanceId, ref MultiList store) where I : IMultiListDataImpl => m_HeadIdIndices.TryGetValue(objectInstanceId, out var headIdIndex) ? store.AddTail(headIdIndex) : (-1, 0); - unsafe int AddTail(int objectInstanceId, ref MultiList store, int* outIds, int count) + unsafe int AddTail(int objectInstanceId, ref MultiList store, int* outIds, int count) where I : IMultiListDataImpl { if (!m_HeadIdIndices.TryGetValue(objectInstanceId, out var headIdIndex)) return 0; return store.AddTail(headIdIndex, outIds, count); } - int GetHeadId(int objectInstanceId, ref MultiList store) + int GetHeadId(int objectInstanceId, ref MultiList store) where I : IMultiListDataImpl { if (!m_HeadIdIndices.TryGetValue(objectInstanceId, out var headIdIndex)) return -1; @@ -133,16 +174,16 @@ int GetHeadId(int objectInstanceId, ref MultiList store) return store.HeadIds[headIdIndex]; } - bool HasHead(int objectInstanceId, ref MultiList store) => + bool HasHead(int objectInstanceId, ref MultiList store) where I : IMultiListDataImpl => GetHeadId(objectInstanceId, ref store) >= 0; - bool GetHeadData(int objectInstanceId, ref MultiList store, ref T data) + bool GetHeadData(int objectInstanceId, ref MultiList store, ref T data) where I : IMultiListDataImpl { var headId = GetHeadId(objectInstanceId, ref store); if (headId < 0) return false; - data = store.Data[headId]; + data = store.Data.Get(headId); return true; } @@ -158,6 +199,18 @@ public bool TryGetPrimaryEntity(int objectInstanceId, out Entity entity) return GetHeadData(objectInstanceId, ref m_Entities, ref entity); } + public Entity RemovePrimaryEntity(int objectInstanceId) + { + int head = GetHeadId(objectInstanceId, ref m_Entities); + if (head == -1) + return Entity.Null; + var entity = m_Entities.Data.Data[head]; + m_FreeHeadIds.Add(head); + m_HeadIdIndices.Remove(objectInstanceId); + m_Entities.ReleaseList(head); + return entity; + } + public (int id, int serial) ReserveAdditionalEntity(int objectInstanceId) => AddTail(objectInstanceId, ref m_Entities); @@ -165,18 +218,17 @@ public unsafe int ReserveAdditionalEntities(int objectInstanceId, int* outIds, i AddTail(objectInstanceId, ref m_Entities, outIds, count); public void RecordAdditionalEntityAt(int atId, Entity entity) => - m_Entities.Data[atId] = entity; + m_Entities.Data.Data[atId] = entity; // returns false if the object is unknown to the conversion system public bool GetEntities(int objectInstanceId, out MultiListEnumerator iter) { var headId = GetHeadId(objectInstanceId, ref m_Entities); - iter = m_Entities.SelectListAt(headId); + iter = new MultiListEnumerator(m_Entities.SelectListAt(headId)); return headId >= 0; } - bool RecordEvent(UnityObject context, ref MultiList eventStore, in T eventData) - where T : IConversionEventData + bool RecordEvent(UnityObject context, ref MultiList> eventStore, in LogEventData eventData) { var instanceId = 0; if (context != null) @@ -197,7 +249,7 @@ public bool RecordLogEvent(UnityObject context, UnityLogType logType, string mes public bool RecordExceptionEvent(UnityObject context, Exception exception) => RecordLogEvent(context, UnityLogType.Exception, $"{exception.GetType().Name}: {exception.Message}"); - MultiListEnumerator SelectJournalData(UnityObject context, ref MultiList store) + MultiListEnumerator SelectJournalData(UnityObject context, ref MultiList store) where I : IMultiListDataImpl { var iter = store.SelectListAt(GetHeadId(context.GetInstanceID(), ref store)); if (!iter.IsValid) @@ -206,7 +258,7 @@ MultiListEnumerator SelectJournalData(UnityObject context, ref MultiList SelectJournalData(MultiList store) + IEnumerable<(int objectInstanceId, T eventData)> SelectJournalData(MultiList> store) { //@TODO: make custom enumerator for this @@ -225,9 +277,9 @@ MultiListEnumerator SelectJournalData(UnityObject context, ref MultiList SelectEntities(UnityObject context) => - SelectJournalData(context, ref m_Entities); + new MultiListEnumerator(SelectJournalData(context, ref m_Entities)); - public MultiListEnumerator SelectLogEventsFast(UnityObject context) => + public MultiListEnumerator> SelectLogEventsFast(UnityObject context) => SelectJournalData(context, ref m_LogEvents); public LogEventData[] SelectLogEventsOrdered(UnityObject context) @@ -260,7 +312,7 @@ public void AddHybridComponent(GameObject gameObject, Type type) m_HybridTypes.Add(index, type); } - public MultiListEnumerator HybridTypes(int headIdIndex) => + public MultiListEnumerator> HybridTypes(int headIdIndex) => m_HybridTypes.SelectList(headIdIndex); public IEnumerable<(int objectInstanceId, LogEventData eventData)> SelectLogEventsFast() => diff --git a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionMappingSystem.cs b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionMappingSystem.cs index 1149bdd4..0027b76a 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionMappingSystem.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionMappingSystem.cs @@ -536,7 +536,7 @@ void ClearIncrementalConversion(GameObject gameObject) m_DstManager.DestroyEntity(entity); } - m_JournalData.RemoveForIncremental(gameObject); + m_JournalData.RemoveForIncremental(gameObject.GetInstanceID(), gameObject); } /// @@ -682,7 +682,7 @@ public void DeclareReferencedAsset(UnityObject asset) EntityManager.SetComponentObject(entity, asset.GetType(), asset); } - public Guid GetGuidForAssetExport(UnityObject asset) + public Hash128 GetGuidForAssetExport(UnityObject asset) { if (!asset.IsAsset()) throw new ArgumentException("Object is not an Asset", nameof(asset)); diff --git a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSettings.cs b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSettings.cs index 1d96c7ad..3421e8a1 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSettings.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSettings.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; +using System.Text; #if UNITY_EDITOR +using UnityEditor; #if UNITY_2020_2_OR_NEWER using UnityEditor.AssetImporters; #else @@ -35,6 +37,18 @@ public class GameObjectConversionSettings public BlobAssetStore BlobAssetStore { get; protected internal set; } + //Export fields + class ExportedAsset + { +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + public Hash128 Guid; + public string AssetPath; + public FileInfo ExportFileInfo; + public bool Exported; +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value + } + Dictionary m_ExportedAssets = new Dictionary(); + public GameObjectConversionSettings() {} // not a clone - only copies what makes sense for creating entities into a separate guid namespace @@ -101,15 +115,63 @@ public World CreateConversionWorld() // ** EXPORTING ** - public bool SupportsExporting - => GetType() == typeof(GameObjectConversionSettings); + public bool SupportsExporting => (GetType() != typeof(GameObjectConversionSettings) || FilterFlags == WorldSystemFilterFlags.DotsRuntimeGameObjectConversion); - public virtual Guid GetGuidForAssetExport(UnityObject uobject) + public virtual Hash128 GetGuidForAssetExport(UnityObject uobject) { if (uobject == null) throw new ArgumentNullException(nameof(uobject)); +#if UNITY_EDITOR + if (!m_ExportedAssets.TryGetValue(uobject, out var found)) + { + var guid = GetGuidForUnityObject(uobject); + if (guid.IsValid) + { + //Use the guid as an extension and retrieve the path to where to save in the AssetDataBase + if (AssetImportContext != null) + { + var exportFileInfo = AssetImportContext.GetResultPath(guid.ToString()); + var assetPath = AssetDatabase.GetAssetPath(uobject); + m_ExportedAssets.Add(uobject, found = new ExportedAsset + { + Guid = guid, + AssetPath = assetPath, + ExportFileInfo = new FileInfo(exportFileInfo), + }); + } + //TODO: Set the exported asset path for LiveLink case because AssetImportContext might still be null + } + } + if(found != null) + return found.Guid; +#endif + return new Hash128(); + } + + internal Hash128 GetGuidForUnityObject(UnityObject obj) + { +#if UNITY_EDITOR + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long fileId)) + return new Hash128(); - return Guid.Empty; + if (!new Hash128(guid).IsValid) + { + // Special case for memory textures + if (obj is UnityEngine.Texture texture) + { + return texture.imageContentsHash; + } + UnityEngine.Debug.LogWarning($"Could not get a Guid for object type '{obj.GetType().FullName}'.", obj); + return new Hash128(); + } + + // Merge asset database guid and file identifier + var hash = UnityEngine.Hash128.Compute(guid); + hash.Append(fileId); + return hash; +#else + return new Hash128(); +#endif } public virtual Stream TryCreateAssetExportWriter(UnityObject uobject) @@ -117,7 +179,20 @@ public virtual Stream TryCreateAssetExportWriter(UnityObject uobject) if (uobject == null) throw new ArgumentNullException(nameof(uobject)); - return null; + if (!m_ExportedAssets.TryGetValue(uobject, out var item)) + { + throw new Exception($"Trying to create export writer for asset {uobject}, but it has never been registered to be exported." + + $"Make sure {nameof(GetGuidForAssetExport)} is being called in a conversion system first before using {nameof(TryCreateAssetExportWriter)} in a conversion system from the {nameof(GameObjectExportGroup)}"); + } + + //if the asset has already been exported, no need to export it twice + if (item.Exported) + return null; + + item.Exported = true; + item.ExportFileInfo.Directory.Create(); + + return item.ExportFileInfo.Create(); } } } diff --git a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSystem.cs b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSystem.cs index 4204d5dd..d70a5573 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSystem.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionSystem.cs @@ -287,7 +287,7 @@ public void AddHybridComponent(UnityEngine.Component component) => // ** EXPORT ** - public Guid GetGuidForAssetExport(UnityObject asset) + public Unity.Entities.Hash128 GetGuidForAssetExport(UnityObject asset) => m_MappingSystem.GetGuidForAssetExport(asset); public Stream TryCreateAssetExportWriter(UnityObject asset) => m_MappingSystem.TryCreateAssetExportWriter(asset); diff --git a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionUtility.cs b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionUtility.cs index 5ca6feb4..08a01811 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionUtility.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/GameObjectConversionUtility.cs @@ -44,7 +44,7 @@ internal static World CreateConversionWorld(GameObjectConversionSettings setting var systemTypes = settings.Systems ?? DefaultWorldInitialization.GetAllSystems(settings.FilterFlags); - var includeExport = settings.GetType() != typeof(GameObjectConversionSettings); + var includeExport = settings.SupportsExporting; AddConversionSystems(gameObjectWorld, systemTypes.Concat(settings.ExtraSystems), includeExport); settings.ConversionWorldCreated?.Invoke(gameObjectWorld); diff --git a/Unity.Entities.Hybrid/GameObjectConversion/JournalDataDebug.cs b/Unity.Entities.Hybrid/GameObjectConversion/JournalDataDebug.cs index 13764688..b6d483cf 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/JournalDataDebug.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/JournalDataDebug.cs @@ -46,7 +46,7 @@ public override int GetHashCode() partial struct ConversionJournalData { - static IEnumerable SelectJournalDataDebug(int objectInstanceId, int headIdIndex, ref MultiList store) => + static IEnumerable SelectJournalDataDebug(int objectInstanceId, int headIdIndex, ref MultiList store) where I : IMultiListDataImpl => store .SelectListAt(store.HeadIds[headIdIndex]) .Select(e => new JournalDataDebug(objectInstanceId, e)); diff --git a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs b/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs index 10309a93..42dff340 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.Linq; +using Unity.Collections; namespace Unity.Entities.Conversion { @@ -32,7 +33,52 @@ public static bool CalcExpandCapacity(int current, ref int needed) } } - struct MultiList + interface IMultiListDataImpl : IDisposable + { + void Init(); + void Resize(int size); + void Set(int idx, in T data); + T Get(int idx); + } + + struct MultiListArrayData : IMultiListDataImpl + { + public T[] Data; + + void IMultiListDataImpl.Init() => Data = Array.Empty(); + void IMultiListDataImpl.Resize(int size) => Array.Resize(ref Data, size); + void IMultiListDataImpl.Set(int idx, in T data) => Data[idx] = data; + T IMultiListDataImpl.Get(int idx) => Data[idx]; + public void Dispose() {} + } + + struct MultiListNativeArrayData : IMultiListDataImpl where T : unmanaged + { + public NativeArray Data; + + void IMultiListDataImpl.Init() { } + + void IMultiListDataImpl.Resize(int size) + { + var newData = new NativeArray(size, Allocator.Persistent); + if (Data.IsCreated) + { + NativeArray.Copy(Data, 0, newData, 0, Data.Length); + Data.Dispose(); + } + Data = newData; + } + void IMultiListDataImpl.Set(int idx, in T data) => Data[idx] = data; + public T Get(int idx) => Data[idx]; + + public void Dispose() + { + if (Data.IsCreated) + Data.Dispose(); + } + } + + struct MultiList : IDisposable where I : IMultiListDataImpl { // `Next` in this list is used for tracking two things // * sublists: Next points to next item in sublist @@ -42,17 +88,29 @@ struct MultiList // `HeadIds` is a front-end index to align a set of MultiLists on a key index, while supporting // different sized sublists across MultiLists. - public int[] HeadIds; - public int[] Next; + public NativeArray HeadIds; + public NativeArray Next; public int NextFree; - public T[] Data; + public I Data; public void Init() { - HeadIds = Array.Empty(); - Next = Array.Empty(); NextFree = -1; - Data = Array.Empty(); + Data.Init(); + SetCapacity(16); + SetHeadIdsCapacity(16); + } + + public void Dispose() + { + if (HeadIds.IsCreated) + HeadIds.Dispose(); + HeadIds = default; + if (Next.IsCreated) + Next.Dispose(); + Next = default; + Data.Dispose(); + Data = default; } // create new sublist, return its id or throw if sublist already exists @@ -66,7 +124,7 @@ public void AddHead(int headIdIndex, in T data) var newId = Alloc(); HeadIds[headIdIndex] = newId; Next[newId] = -1; - Data[newId] = data; + Data.Set(newId, in data); } // either add a head or insert at front (not tail!) of an existing list (returns id) @@ -85,8 +143,7 @@ public void Add(int headIdIndex, in T data) Next[newId] = Next[headId]; Next[headId] = newId; } - - Data[newId] = data; + Data.Set(newId, in data); } public (int id, int serial) AddTail(int headIdIndex) @@ -129,7 +186,7 @@ public unsafe int AddTail(int headIdIndex, int* outIds, int count) public (int id, int serial) AddTail(int headIdIndex, in T data) { var added = AddTail(headIdIndex); - Data[added.id] = data; + Data.Set(added.id, in data); return added; } @@ -172,19 +229,19 @@ void Release(int id) } [Pure] - public MultiListEnumerator SelectListAt(int headId) => - new MultiListEnumerator(Data, Next, headId); + public MultiListEnumerator SelectListAt(int headId) => + new MultiListEnumerator(Data, Next, headId); [Pure] - public MultiListEnumerator SelectList(int headIdIndex) => - new MultiListEnumerator(Data, Next, HeadIds[headIdIndex]); + public MultiListEnumerator SelectList(int headIdIndex) => + new MultiListEnumerator(Data, Next, HeadIds[headIdIndex]); - public bool TrySelectList(int headIdIndex, out MultiListEnumerator iter) + public bool TrySelectList(int headIdIndex, out MultiListEnumerator iter) { var headId = HeadIds[headIdIndex]; if (headId < 0) { - iter = MultiListEnumerator.Empty; + iter = MultiListEnumerator.Empty; return false; } @@ -198,10 +255,21 @@ public void EnsureCapacity(int capacity) SetCapacity(capacity); } + static void Resize(ref NativeArray data, int size) + { + var newData = new NativeArray(size, Allocator.Persistent); + if (data.IsCreated) + { + NativeArray.Copy(data, 0, newData, 0, data.Length < size ? data.Length : size); + data.Dispose(); + } + data = newData; + } + public void SetHeadIdsCapacity(int newCapacity) { var oldCapacity = HeadIds.Length; - Array.Resize(ref HeadIds, newCapacity); + Resize(ref HeadIds, newCapacity); for (var i = oldCapacity; i < newCapacity; ++i) HeadIds[i] = -1; @@ -240,8 +308,8 @@ void SetCapacity(int newCapacity) { var oldCapacity = Next.Length; - Array.Resize(ref Next, newCapacity); - Array.Resize(ref Data, newCapacity); + Resize(ref Next, newCapacity); + Data.Resize(newCapacity); for (var i = oldCapacity; i < newCapacity; ++i) Next[i] = i + 1; @@ -251,7 +319,7 @@ void SetCapacity(int newCapacity) } } - class MultiListEnumeratorDebugView + class MultiListEnumeratorDebugView where T : unmanaged { MultiListEnumerator m_Enumerator; @@ -264,16 +332,15 @@ public MultiListEnumeratorDebugView(MultiListEnumerator enumerator) public T[] Items => m_Enumerator.ToArray(); } - [DebuggerTypeProxy(typeof(MultiListEnumeratorDebugView<>))] - public struct MultiListEnumerator : IEnumerable, IEnumerator + struct MultiListEnumerator : IEnumerable, IEnumerator where I : IMultiListDataImpl { - T[] m_Data; - int[] m_Next; + I m_Data; + NativeArray m_Next; int m_StartIndex; int m_CurIndex; bool m_IsFirst; - internal MultiListEnumerator(T[] data, int[] next, int startIndex) + internal MultiListEnumerator(I data, NativeArray next, int startIndex) { m_Data = data; m_Next = next; @@ -284,7 +351,7 @@ internal MultiListEnumerator(T[] data, int[] next, int startIndex) public void Dispose() {} - public static MultiListEnumerator Empty => new MultiListEnumerator(null, null, -1); + public static MultiListEnumerator Empty => new MultiListEnumerator(default, default, -1); IEnumerator IEnumerable.GetEnumerator() => this; IEnumerator IEnumerable.GetEnumerator() => this; @@ -308,7 +375,7 @@ public void Reset() m_IsFirst = true; } - public T Current => m_Data[m_CurIndex]; + public T Current => m_Data.Get(m_CurIndex); object IEnumerator.Current => Current; public bool IsEmpty => m_StartIndex < 0; @@ -325,9 +392,44 @@ public int Count() } } + [DebuggerTypeProxy(typeof(MultiListEnumeratorDebugView<>))] + public struct MultiListEnumerator : IEnumerable, IEnumerator where T : unmanaged + { + MultiListEnumerator> m_Enumerator; + + internal MultiListEnumerator(MultiListEnumerator> enumerator) + { + m_Enumerator = enumerator; + } + internal MultiListEnumerator(MultiListNativeArrayData data, NativeArray next, int startIndex) + { + m_Enumerator = new MultiListEnumerator>(data, next, startIndex); + } + + public void Dispose() => m_Enumerator.Dispose(); + + public static MultiListEnumerator Empty => new MultiListEnumerator(default, default, -1); + + IEnumerator IEnumerable.GetEnumerator() => this; + IEnumerator IEnumerable.GetEnumerator() => this; + + public bool MoveNext() => m_Enumerator.MoveNext(); + + public void Reset() => m_Enumerator.Reset(); + + public T Current => m_Enumerator.Current; + object IEnumerator.Current => Current; + + public bool IsEmpty => m_Enumerator.IsEmpty; + public bool Any => !IsEmpty; + public bool IsValid => m_Enumerator.IsValid; + + public int Count() => m_Enumerator.Count(); + } + static class MultiListDebugUtility { - public static void ValidateIntegrity(ref MultiList multiList) + public static void ValidateIntegrity(ref MultiList multiList) where I : IMultiListDataImpl { var freeList = new List(); for (var i = multiList.NextFree; i >= 0; i = multiList.Next[i]) @@ -340,7 +442,7 @@ public static void ValidateIntegrity(ref MultiList multiList) throw new InvalidOperationException(); } - public static IEnumerable> SelectAllLists(int[] headIds, int[] next) + public static IEnumerable> SelectAllLists(NativeArray headIds, NativeArray next) { foreach (var headId in headIds) { @@ -356,11 +458,11 @@ public static IEnumerable> SelectAllLists(int[] headIds, int[] next) } } - public static IEnumerable> SelectAllData(MultiList multiList) + public static IEnumerable> SelectAllData(MultiList multiList) where I : IMultiListDataImpl { var data = multiList.Data; foreach (var list in SelectAllLists(multiList.HeadIds, multiList.Next)) - yield return new List(list.Select(i => data[i])); + yield return new List(list.Select(i => data.Get(i))); } } } diff --git a/Unity.Entities.PerformanceTests/EntityCommandBufferPerformanceTests.cs b/Unity.Entities.PerformanceTests/EntityCommandBufferPerformanceTests.cs index e857fb5a..166c3000 100644 --- a/Unity.Entities.PerformanceTests/EntityCommandBufferPerformanceTests.cs +++ b/Unity.Entities.PerformanceTests/EntityCommandBufferPerformanceTests.cs @@ -298,7 +298,7 @@ public void EntityCommandBuffer_DestroyEntity([Values(10, 1000, 10000)] int size } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -322,7 +322,7 @@ public void EntityCommandBuffer_DestroyEntity([Values(10, 1000, 10000)] int size ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -355,7 +355,7 @@ public void EntityCommandBuffer_CreateEntities([Values(10, 1000, 10000)] int siz FillWithCreateEntityCommands(ecb, size); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -377,7 +377,7 @@ public void EntityCommandBuffer_CreateEntities([Values(10, 1000, 10000)] int siz ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -406,7 +406,7 @@ public void EntityCommandBuffer_InstantiateEntities([Values(10, 1000, 10000)] in FillWithInstantiateEntityCommands(ecb, size, prefabEntity); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -429,7 +429,7 @@ public void EntityCommandBuffer_InstantiateEntities([Values(10, 1000, 10000)] in ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -461,7 +461,7 @@ public void EntityCommandBuffer_AddComponent([Values(10, 1000, 10000)] int size) } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -485,7 +485,7 @@ public void EntityCommandBuffer_AddComponent([Values(10, 1000, 10000)] int size) ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -521,7 +521,7 @@ public void EntityCommandBuffer_SetComponent([Values(10, 1000, 10000)] int size) } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -545,7 +545,7 @@ public void EntityCommandBuffer_SetComponent([Values(10, 1000, 10000)] int size) ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -581,7 +581,7 @@ public void EntityCommandBuffer_RemoveComponent([Values(10, 1000, 10000)] int si } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -605,7 +605,7 @@ public void EntityCommandBuffer_RemoveComponent([Values(10, 1000, 10000)] int si ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -644,7 +644,7 @@ public void EntityCommandBuffer_AddSharedComponent([Values(10, 1000, 10000)] int } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -668,7 +668,7 @@ public void EntityCommandBuffer_AddSharedComponent([Values(10, 1000, 10000)] int ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -705,7 +705,7 @@ public void EntityCommandBuffer_AddManagedComponent([Values(10, 1000, 10000)] in } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -729,7 +729,7 @@ public void EntityCommandBuffer_AddManagedComponent([Values(10, 1000, 10000)] in ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -767,7 +767,7 @@ public void EntityCommandBuffer_SetSharedComponent([Values(10, 1000, 10000)] int } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -791,7 +791,7 @@ public void EntityCommandBuffer_SetSharedComponent([Values(10, 1000, 10000)] int ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -828,7 +828,7 @@ public void EntityCommandBuffer_SetManagedComponent([Values(10, 1000, 10000)] in } }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -852,7 +852,7 @@ public void EntityCommandBuffer_SetManagedComponent([Values(10, 1000, 10000)] in ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -887,7 +887,7 @@ public void EntityCommandBuffer_AddComponentToEntityQuery([Values(10, 1000, 1000 ecb.AddComponent(group, typeof(EcsTestData2)); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -911,7 +911,7 @@ public void EntityCommandBuffer_AddComponentToEntityQuery([Values(10, 1000, 1000 ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -941,7 +941,7 @@ public void EntityCommandBuffer_RemoveComponentFromEntityQuery([Values(10, 1000, ecb.RemoveComponent(group, typeof(EcsTestData)); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -965,7 +965,7 @@ public void EntityCommandBuffer_RemoveComponentFromEntityQuery([Values(10, 1000, ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -995,7 +995,7 @@ public void EntityCommandBuffer_DestroyEntitiesInEntityQuery([Values(10, 1000, 1 ecb.DestroyEntity(group); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -1019,7 +1019,7 @@ public void EntityCommandBuffer_DestroyEntitiesInEntityQuery([Values(10, 1000, 1 ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -1049,7 +1049,7 @@ public void EntityCommandBuffer_AddSharedComponentToEntityQuery([Values(10, 1000 ecb.AddSharedComponent(group, new EcsTestSharedComp {value = 1}); }) .SampleGroup("Record") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { @@ -1073,7 +1073,7 @@ public void EntityCommandBuffer_AddSharedComponentToEntityQuery([Values(10, 1000 ecb.Playback(m_Manager); }) .SampleGroup("Playback") - .WarmupCount(0) + .WarmupCount(1) .MeasurementCount(100) .SetUp(() => { diff --git a/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs b/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs new file mode 100644 index 00000000..635bd185 --- /dev/null +++ b/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Burst; +using Unity.Collections; +using Unity.PerformanceTesting; +using Unity.Entities.Tests; +using Unity.Jobs; + +namespace Unity.Entities.PerformanceTests +{ + [TestFixture] + [Category("Performance")] + public sealed class EntityQueryIncrementalCachingPerformanceTests : EntityPerformanceTestFixture + { + struct TestTag0 : IComponentData + { + } + + struct TestTag1 : IComponentData + { + } + + struct TestTag2 : IComponentData + { + } + + struct TestTag3 : IComponentData + { + } + + struct TestTag4 : IComponentData + { + } + + struct TestTag5 : IComponentData + { + } + + struct TestTag6 : IComponentData + { + } + + struct TestTag7 : IComponentData + { + } + + struct TestTag8 : IComponentData + { + } + + struct TestTag9 : IComponentData + { + } + + struct TestTag10 : IComponentData + { + } + + struct TestTag11 : IComponentData + { + } + + struct TestTag12 : IComponentData + { + } + + struct TestTag13 : IComponentData + { + } + + struct TestTag14 : IComponentData + { + } + + struct TestTag15 : IComponentData + { + } + + struct TestTag16 : IComponentData + { + } + + struct TestTag17 : IComponentData + { + } + + Type[] TagTypes = + { + typeof(TestTag0), + typeof(TestTag1), + typeof(TestTag2), + typeof(TestTag3), + typeof(TestTag4), + typeof(TestTag5), + typeof(TestTag6), + typeof(TestTag7), + typeof(TestTag8), + typeof(TestTag9), + typeof(TestTag10), + typeof(TestTag11), + typeof(TestTag12), + typeof(TestTag13), + typeof(TestTag14), + typeof(TestTag15), + typeof(TestTag16), + typeof(TestTag17), + }; + + NativeArray CreateUniqueArchetypes(int size) + { + var archetypes = new NativeArray(size, Allocator.Persistent); + + for (int i = 0; i < size; i++) + { + var typeCount = CollectionHelper.Log2Ceil(i); + var typeList = new List(); + for (int typeIndex = 0; typeIndex < typeCount; typeIndex++) + { + if ((i & (1 << typeIndex)) != 0) + typeList.Add(TagTypes[typeIndex]); + } + + typeList.Add(typeof(EcsTestData)); + typeList.Add(typeof(EcsTestSharedComp)); + + var types = typeList.ToArray(); + archetypes[i] = m_Manager.CreateArchetype(types); + } + + return archetypes; + } + + NativeArray CreateUniqueQueries(int size) + { + var queries = new NativeArray(size, Allocator.Persistent); + + for (int i = 0; i < size; i++) + { + var typeCount = CollectionHelper.Log2Ceil(i); + var typeList = new List(); + for (int typeIndex = 0; typeIndex < typeCount; typeIndex++) + { + if ((i & (1 << typeIndex)) != 0) + typeList.Add(TagTypes[typeIndex]); + } + + typeList.Add(typeof(EcsTestData)); + typeList.Add(typeof(EcsTestSharedComp)); + + var types = typeList.ToArray(); + queries[i] = m_Manager.CreateEntityQuery(types); + } + + return queries; + } + + [Test, Performance] + public void CreateDestroyEntity_Scaling([Values(10, 100)] int archetypeCount, [Values(10, 100)] int queryCount) + { + const int kInitialEntityCount = 5000000; + const int kCreateDestroyEntityCount = 200000; + + var archetypes = CreateUniqueArchetypes(archetypeCount); + var queries = CreateUniqueQueries(queryCount); + + for (int archetypeIndex = 0; archetypeIndex < archetypeCount; ++archetypeIndex) + { + m_Manager.CreateEntity(archetypes[archetypeIndex], kInitialEntityCount / archetypeCount, Allocator.Temp); + } + + var basicArchetype = m_Manager.CreateArchetype(typeof(EcsTestData)); + + var createEntities = default(NativeArray); + Measure.Method(() => { createEntities = m_Manager.CreateEntity(basicArchetype, kCreateDestroyEntityCount, Allocator.Temp); }) + .CleanUp(() => { m_Manager.DestroyEntity(createEntities); createEntities.Dispose(); }) + .WarmupCount(1) + .SampleGroup("CreateEntities") + .Run(); + + var destroyEntities = default(NativeArray); + Measure.Method(() => { m_Manager.DestroyEntity(destroyEntities); }) + .SetUp(() => { destroyEntities = m_Manager.CreateEntity(basicArchetype, kCreateDestroyEntityCount, Allocator.Temp); }) + .CleanUp(() => { destroyEntities.Dispose(); }) + .WarmupCount(1) + .SampleGroup("DestroyEntities") + .Run(); + + archetypes.Dispose(); + queries.Dispose(); + } + + [BurstCompile(CompileSynchronously = true)] + struct TestJob : IJobEntityBatch + { + public ComponentTypeHandle EcsTestDataRW; + + public void Execute(ArchetypeChunk batchInChunk, int batchIndex) + { + var data = batchInChunk.GetNativeArray(EcsTestDataRW); + data[0] = new EcsTestData {value = 10}; + } + } + + [Test, Performance] + public unsafe void IJobEntityBatch_Scheduling([Values(100, 10000, 5000000)] int entityCount, [Values(10, 100)] int archetypeCount) + { + var archetypes = CreateUniqueArchetypes(archetypeCount); + + for (int archetypeIndex = 0; archetypeIndex < archetypeCount; ++archetypeIndex) + { + m_Manager.CreateEntity(archetypes[archetypeIndex], entityCount / archetypeCount, Allocator.Temp); + } + + var basicQuery = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + var handle = default(JobHandle); + Measure.Method(() => + { + handle = new TestJob + { + EcsTestDataRW = m_Manager.GetComponentTypeHandle(false) + }.ScheduleParallel(basicQuery, 1, handle); + }) + .CleanUp(() => + { + handle.Complete(); + }) + .WarmupCount(1) + .Run(); + + archetypes.Dispose(); + } + + [Test, Performance] + public unsafe void IJobEntityBatch_Executing([Values(100, 10000, 5000000)] int entityCount, [Values(10, 100)] int archetypeCount) + { + var archetypes = CreateUniqueArchetypes(archetypeCount); + + for (int archetypeIndex = 0; archetypeIndex < archetypeCount; ++archetypeIndex) + { + m_Manager.CreateEntity(archetypes[archetypeIndex], entityCount / archetypeCount, Allocator.Temp); + } + + var basicQuery = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + Measure.Method(() => + { + new TestJob + { + EcsTestDataRW = m_Manager.GetComponentTypeHandle(false) + }.Run(basicQuery); + }) + .WarmupCount(1) + .Run(); + + + archetypes.Dispose(); + } + } +} diff --git a/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs.meta b/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs.meta new file mode 100644 index 00000000..078ed613 --- /dev/null +++ b/Unity.Entities.PerformanceTests/EntityQueryIncrementalCachingPerformanceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 213a761380c9f4e1db44403dc2f009dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities.Tests/BurstCompatibilityTests.gen.cs b/Unity.Entities.Tests/BurstCompatibilityTests.gen.cs deleted file mode 100644 index b3468b5e..00000000 --- a/Unity.Entities.Tests/BurstCompatibilityTests.gen.cs +++ /dev/null @@ -1,1648 +0,0 @@ -// auto-generated -#if !NET_DOTS -using System; -using NUnit.Framework; -using Unity.Burst; -using Unity.Entities; -using Unity.Collections; -using System.ComponentModel; -namespace Unity.Entities.BurstTests -{ -[BurstCompile] -public unsafe class BurstCompatibilityTests_Generated -{ - private delegate void TestFunc(IntPtr p); - public static readonly string Guid = "de88b99578b2977946e15cda75b2e13c"; - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddComponent_overload0(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddComponent(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddComponent_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddComponent_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddComponentDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange_overload0(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddComponentDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddComponentDuringStructuralChange_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddComponentsDuringStructuralChange(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentTypes); - var instance = (EntityDataAccess*)p; - instance->AddComponentsDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddComponentsDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddComponentsDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddSharedComponentData(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(int); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddSharedComponentData(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddSharedComponentData() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddSharedComponentData); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_AddSharedComponentDataDuringStructuralChange(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(int); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->AddSharedComponentDataDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_AddSharedComponentDataDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_AddSharedComponentDataDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_BeforeStructuralChange(IntPtr p) - { - var instance = (EntityDataAccess*)p; - instance->BeforeStructuralChange(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_BeforeStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_BeforeStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateArchetype(IntPtr p) - { -ComponentType* v0 = (ComponentType*) ((byte*)p + 0); -var v1 = default(int); - var instance = (EntityDataAccess*)p; - instance->CreateArchetype(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateArchetype() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateArchetype); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntity(IntPtr p) - { -var v0 = default(EntityArchetype); - var instance = (EntityDataAccess*)p; - instance->CreateEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntity() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntity); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntity_overload0(IntPtr p) - { -var v0 = default(EntityArchetype); -var v1 = default(NativeArray); - var instance = (EntityDataAccess*)p; - instance->CreateEntity(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntity_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntity_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntity_overload1(IntPtr p) - { -var v0 = default(EntityArchetype); -Entity* v1 = (Entity*) ((byte*)p + 1024); -var v2 = default(int); - var instance = (EntityDataAccess*)p; - instance->CreateEntity(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntity_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntity_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange(IntPtr p) - { -var v0 = default(EntityArchetype); - var instance = (EntityDataAccess*)p; - instance->CreateEntityDuringStructuralChange(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload0(IntPtr p) - { -var v0 = default(EntityArchetype); -var v1 = default(NativeArray); - var instance = (EntityDataAccess*)p; - instance->CreateEntityDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload1(IntPtr p) - { -var v0 = default(EntityArchetype); -Entity* v1 = (Entity*) ((byte*)p + 1024); -var v2 = default(int); - var instance = (EntityDataAccess*)p; - instance->CreateEntityDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_CreateEntityDuringStructuralChange_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_DestroyEntity(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityDataAccess*)p; - instance->DestroyEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_DestroyEntity() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_DestroyEntity); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_DestroyEntity_overload0(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); - var instance = (EntityDataAccess*)p; - instance->DestroyEntity(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_DestroyEntity_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_DestroyEntity_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_DestroyEntityDuringStructuralChange(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); - var instance = (EntityDataAccess*)p; - instance->DestroyEntityDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_DestroyEntityDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_DestroyEntityDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_DestroyEntityInternal(IntPtr p) - { -Entity* v0 = (Entity*) ((byte*)p + 0); -var v1 = default(int); - var instance = (EntityDataAccess*)p; - instance->DestroyEntityInternal(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_DestroyEntityInternal() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_DestroyEntityInternal); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_DestroyEntityInternalDuringStructuralChange(IntPtr p) - { -Entity* v0 = (Entity*) ((byte*)p + 0); -var v1 = default(int); - var instance = (EntityDataAccess*)p; - instance->DestroyEntityInternalDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_DestroyEntityInternalDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_DestroyEntityInternalDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_Exists(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityDataAccess*)p; - instance->Exists(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_Exists() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_Exists); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_FillSortedArchetypeArray(IntPtr p) - { -ComponentTypeInArchetype* v0 = (ComponentTypeInArchetype*) ((byte*)p + 0); -ComponentType* v1 = (ComponentType*) ((byte*)p + 1024); -var v2 = default(int); -EntityDataAccess.FillSortedArchetypeArray(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_FillSortedArchetypeArray() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_FillSortedArchetypeArray); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_GetComponentDataRawRW(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityDataAccess*)p; - instance->GetComponentDataRawRW(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_GetComponentDataRawRW() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_GetComponentDataRawRW); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_GetComponentDataRawRWEntityHasComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityDataAccess*)p; - instance->GetComponentDataRawRWEntityHasComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_GetComponentDataRawRWEntityHasComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_GetComponentDataRawRWEntityHasComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_GetEntityOnlyArchetype(IntPtr p) - { - var instance = (EntityDataAccess*)p; - instance->GetEntityOnlyArchetype(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_GetEntityOnlyArchetype() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_GetEntityOnlyArchetype); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_HasComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->HasComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_HasComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_HasComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_InstantiateInternal(IntPtr p) - { -var v0 = default(Entity); -Entity* v1 = (Entity*) ((byte*)p + 1024); -var v2 = default(int); - var instance = (EntityDataAccess*)p; - instance->InstantiateInternal(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_InstantiateInternal() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_InstantiateInternal); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_InstantiateInternal_overload0(IntPtr p) - { -Entity* v0 = (Entity*) ((byte*)p + 0); -Entity* v1 = (Entity*) ((byte*)p + 1024); -var v2 = default(int); -var v3 = default(int); -var v4 = default(bool); - var instance = (EntityDataAccess*)p; - instance->InstantiateInternal(v0 ,v1 ,v2 ,v3 ,v4); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_InstantiateInternal_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_InstantiateInternal_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_InstantiateInternalDuringStructuralChange(IntPtr p) - { -var v0 = default(Entity); -Entity* v1 = (Entity*) ((byte*)p + 1024); -var v2 = default(int); - var instance = (EntityDataAccess*)p; - instance->InstantiateInternalDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_InstantiateInternalDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_InstantiateInternalDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_PlaybackManagedChanges(IntPtr p) - { - var instance = (EntityDataAccess*)p; - instance->PlaybackManagedChanges(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_PlaybackManagedChanges() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_PlaybackManagedChanges); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponent_overload0(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponent_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponent_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponent_overload1(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponent(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponent_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponent_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponentDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload0(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponentDuringStructuralChange(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload1(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentType); - var instance = (EntityDataAccess*)p; - instance->RemoveComponentDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveComponentDuringStructuralChange_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_RemoveMultipleComponentsDuringStructuralChange(IntPtr p) - { -var v0 = default(UnsafeMatchingArchetypePtrList); -var v1 = default(EntityQueryFilter); -var v2 = default(ComponentTypes); - var instance = (EntityDataAccess*)p; - instance->RemoveMultipleComponentsDuringStructuralChange(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_RemoveMultipleComponentsDuringStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_RemoveMultipleComponentsDuringStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_SetBufferRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); -BufferHeader* v2 = (BufferHeader*) ((byte*)p + 2048); -var v3 = default(int); - var instance = (EntityDataAccess*)p; - instance->SetBufferRaw(v0 ,v1 ,v2 ,v3); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_SetBufferRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_SetBufferRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_SetComponentDataRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); -void* v2 = (void*) ((byte*)p + 2048); -var v3 = default(int); - var instance = (EntityDataAccess*)p; - instance->SetComponentDataRaw(v0 ,v1 ,v2 ,v3); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_SetComponentDataRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_SetComponentDataRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityDataAccess_SwapComponents(IntPtr p) - { -var v0 = default(ArchetypeChunk); -var v1 = default(int); -var v2 = default(ArchetypeChunk); -var v3 = default(int); - var instance = (EntityDataAccess*)p; - instance->SwapComponents(v0 ,v1 ,v2 ,v3); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityDataAccess_SwapComponents() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityDataAccess_SwapComponents); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AddComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->AddComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AddComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AddComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AddComponent_overload0(IntPtr p) - { -var v0 = default(EntityQuery); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->AddComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AddComponent_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AddComponent_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AddComponent_overload1(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->AddComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AddComponent_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AddComponent_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AddComponentRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->AddComponentRaw(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AddComponentRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AddComponentRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AddComponents(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentTypes); - var instance = (EntityManager*)p; - instance->AddComponents(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AddComponents() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AddComponents); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_AllocateConsecutiveEntitiesForLoading(IntPtr p) - { -var v0 = default(int); - var instance = (EntityManager*)p; - instance->AllocateConsecutiveEntitiesForLoading(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_AllocateConsecutiveEntitiesForLoading() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_AllocateConsecutiveEntitiesForLoading); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_BeforeStructuralChange(IntPtr p) - { - var instance = (EntityManager*)p; - instance->BeforeStructuralChange(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_BeforeStructuralChange() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_BeforeStructuralChange); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_BeginExclusiveEntityTransaction(IntPtr p) - { - var instance = (EntityManager*)p; - instance->BeginExclusiveEntityTransaction(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_BeginExclusiveEntityTransaction() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_BeginExclusiveEntityTransaction); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CompleteAllJobs(IntPtr p) - { - var instance = (EntityManager*)p; - instance->CompleteAllJobs(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CompleteAllJobs() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CompleteAllJobs); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CopyEntities(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(NativeArray); - var instance = (EntityManager*)p; - instance->CopyEntities(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CopyEntities() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CopyEntities); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateArchetype(IntPtr p) - { -ComponentType* v0 = (ComponentType*) ((byte*)p + 0); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->CreateArchetype(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateArchetype() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateArchetype); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntity(IntPtr p) - { - var instance = (EntityManager*)p; - instance->CreateEntity(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntity() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntity); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntity_overload0(IntPtr p) - { -var v0 = default(EntityArchetype); - var instance = (EntityManager*)p; - instance->CreateEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntity_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntity_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntity_overload1(IntPtr p) - { -var v0 = default(EntityArchetype); -var v1 = default(NativeArray); - var instance = (EntityManager*)p; - instance->CreateEntity(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntity_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntity_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntity_overload2(IntPtr p) - { -var v0 = default(EntityArchetype); -var v1 = default(int); -var v2 = default(Allocator); - var instance = (EntityManager*)p; - instance->CreateEntity(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntity_overload2() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntity_overload2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntityManagerInUninitializedState(IntPtr p) - { -EntityManager.CreateEntityManagerInUninitializedState(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntityManagerInUninitializedState() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntityManagerInUninitializedState); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_CreateEntityRemapArray(IntPtr p) - { -var v0 = default(Allocator); - var instance = (EntityManager*)p; - instance->CreateEntityRemapArray(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_CreateEntityRemapArray() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_CreateEntityRemapArray); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_DestroyEntity(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->DestroyEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_DestroyEntity() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_DestroyEntity); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_DestroyEntity_overload0(IntPtr p) - { -var v0 = default(EntityQuery); - var instance = (EntityManager*)p; - instance->DestroyEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_DestroyEntity_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_DestroyEntity_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_DestroyEntity_overload1(IntPtr p) - { -var v0 = default(NativeArray); - var instance = (EntityManager*)p; - instance->DestroyEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_DestroyEntity_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_DestroyEntity_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_DestroyEntity_overload2(IntPtr p) - { -var v0 = default(NativeSlice); - var instance = (EntityManager*)p; - instance->DestroyEntity(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_DestroyEntity_overload2() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_DestroyEntity_overload2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_DestroyEntityInternal(IntPtr p) - { -Entity* v0 = (Entity*) ((byte*)p + 0); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->DestroyEntityInternal(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_DestroyEntityInternal() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_DestroyEntityInternal); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_EndExclusiveEntityTransaction(IntPtr p) - { - var instance = (EntityManager*)p; - instance->EndExclusiveEntityTransaction(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_EndExclusiveEntityTransaction() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_EndExclusiveEntityTransaction); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Equals(IntPtr p) - { -var v0 = default(EntityManager); - var instance = (EntityManager*)p; - instance->Equals(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Equals() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Equals); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Exists(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->Exists(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Exists() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Exists); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetAllArchetypes(IntPtr p) - { -var v0 = default(NativeList); - var instance = (EntityManager*)p; - instance->GetAllArchetypes(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetAllArchetypes() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetAllArchetypes); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetAllChunks(IntPtr p) - { -var v0 = default(Allocator); - var instance = (EntityManager*)p; - instance->GetAllChunks(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetAllChunks() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetAllChunks); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetAllEntities(IntPtr p) - { -var v0 = default(Allocator); - var instance = (EntityManager*)p; - instance->GetAllEntities(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetAllEntities() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetAllEntities); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetBufferLength(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetBufferLength(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetBufferLength() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetBufferLength); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetBufferRawRO(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetBufferRawRO(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetBufferRawRO() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetBufferRawRO); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetBufferRawRW(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetBufferRawRW(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetBufferRawRW() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetBufferRawRW); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetCheckedEntityDataAccess(IntPtr p) - { - var instance = (EntityManager*)p; - instance->GetCheckedEntityDataAccess(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetCheckedEntityDataAccess() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetCheckedEntityDataAccess); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetChunk(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->GetChunk(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetChunk() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetChunk); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetChunkVersionHash(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->GetChunkVersionHash(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetChunkVersionHash() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetChunkVersionHash); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetComponentCount(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->GetComponentCount(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetComponentCount() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetComponentCount); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetComponentDataRawRO(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetComponentDataRawRO(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetComponentDataRawRO() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetComponentDataRawRO); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetComponentDataRawRW(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetComponentDataRawRW(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetComponentDataRawRW() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetComponentDataRawRW); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetComponentTypeIndex(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->GetComponentTypeIndex(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetComponentTypeIndex() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetComponentTypeIndex); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetComponentTypes(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(Allocator); - var instance = (EntityManager*)p; - instance->GetComponentTypes(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetComponentTypes() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetComponentTypes); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntities(IntPtr p) - { -var v0 = default(NativeList); -var v1 = default(NativeList); -var v2 = default(NativeList); - var instance = (EntityManager*)p; - instance->GetCreatedAndDestroyedEntities(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntities() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntities); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntitiesAsync(IntPtr p) - { -var v0 = default(NativeList); -var v1 = default(NativeList); -var v2 = default(NativeList); - var instance = (EntityManager*)p; - instance->GetCreatedAndDestroyedEntitiesAsync(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntitiesAsync() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetCreatedAndDestroyedEntitiesAsync); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetDynamicComponentTypeHandle(IntPtr p) - { -var v0 = default(ComponentType); - var instance = (EntityManager*)p; - instance->GetDynamicComponentTypeHandle(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetDynamicComponentTypeHandle() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetDynamicComponentTypeHandle); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetEnabled(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->GetEnabled(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetEnabled() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetEnabled); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetEntityQueryMask(IntPtr p) - { -var v0 = default(EntityQuery); - var instance = (EntityManager*)p; - instance->GetEntityQueryMask(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetEntityQueryMask() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetEntityQueryMask); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetEntityTypeHandle(IntPtr p) - { - var instance = (EntityManager*)p; - instance->GetEntityTypeHandle(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetEntityTypeHandle() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetEntityTypeHandle); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_GetHashCode(IntPtr p) - { - var instance = (EntityManager*)p; - instance->GetHashCode(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_GetHashCode() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_GetHashCode); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_HasComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->HasComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_HasComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_HasComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_HasComponentRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->HasComponentRaw(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_HasComponentRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_HasComponentRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Instantiate(IntPtr p) - { -var v0 = default(Entity); - var instance = (EntityManager*)p; - instance->Instantiate(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Instantiate() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Instantiate); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Instantiate_overload0(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(NativeArray); - var instance = (EntityManager*)p; - instance->Instantiate(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Instantiate_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Instantiate_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Instantiate_overload1(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(NativeArray); - var instance = (EntityManager*)p; - instance->Instantiate(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Instantiate_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Instantiate_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_Instantiate_overload2(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); -var v2 = default(Allocator); - var instance = (EntityManager*)p; - instance->Instantiate(v0 ,v1 ,v2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_Instantiate_overload2() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_Instantiate_overload2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_IsQueryValid(IntPtr p) - { -var v0 = default(EntityQuery); - var instance = (EntityManager*)p; - instance->IsQueryValid(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_IsQueryValid() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_IsQueryValid); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_PreDisposeCheck(IntPtr p) - { - var instance = (EntityManager*)p; - instance->PreDisposeCheck(); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_PreDisposeCheck() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_PreDisposeCheck); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_RemoveComponent(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_RemoveComponent() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_RemoveComponent); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_RemoveComponent_overload0(IntPtr p) - { -var v0 = default(EntityQuery); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_RemoveComponent_overload0() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_RemoveComponent_overload0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_RemoveComponent_overload1(IntPtr p) - { -var v0 = default(EntityQuery); -var v1 = default(ComponentTypes); - var instance = (EntityManager*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_RemoveComponent_overload1() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_RemoveComponent_overload1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_RemoveComponent_overload2(IntPtr p) - { -var v0 = default(NativeArray); -var v1 = default(ComponentType); - var instance = (EntityManager*)p; - instance->RemoveComponent(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_RemoveComponent_overload2() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_RemoveComponent_overload2); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_RemoveComponentRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); - var instance = (EntityManager*)p; - instance->RemoveComponentRaw(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_RemoveComponentRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_RemoveComponentRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_SetArchetype(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(EntityArchetype); - var instance = (EntityManager*)p; - instance->SetArchetype(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_SetArchetype() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_SetArchetype); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_SetComponentDataRaw(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(int); -void* v2 = (void*) ((byte*)p + 2048); -var v3 = default(int); - var instance = (EntityManager*)p; - instance->SetComponentDataRaw(v0 ,v1 ,v2 ,v3); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_SetComponentDataRaw() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_SetComponentDataRaw); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_SetEnabled(IntPtr p) - { -var v0 = default(Entity); -var v1 = default(bool); - var instance = (EntityManager*)p; - instance->SetEnabled(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_SetEnabled() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_SetEnabled); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityManager_SwapComponents(IntPtr p) - { -var v0 = default(ArchetypeChunk); -var v1 = default(int); -var v2 = default(ArchetypeChunk); -var v3 = default(int); - var instance = (EntityManager*)p; - instance->SwapComponents(v0 ,v1 ,v2 ,v3); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityManager_SwapComponents() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityManager_SwapComponents); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityQueryImpl_GetBufferSafetyHandle(IntPtr p) - { -var v0 = default(int); - var instance = (EntityQueryImpl*)p; - instance->GetBufferSafetyHandle(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityQueryImpl_GetBufferSafetyHandle() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityQueryImpl_GetBufferSafetyHandle); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__EntityQueryImpl_GetSafetyHandle(IntPtr p) - { -var v0 = default(int); - var instance = (EntityQueryImpl*)p; - instance->GetSafetyHandle(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__EntityQueryImpl_GetSafetyHandle() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__EntityQueryImpl_GetSafetyHandle); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_CallForwardingFunction(IntPtr p) - { -SystemState* v0 = (SystemState*) ((byte*)p + 0); -var v1 = default(int); -SystemBaseRegistry.CallForwardingFunction(v0 ,v1); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_CallForwardingFunction() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_CallForwardingFunction); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_CallOnCreate(IntPtr p) - { -SystemState* v0 = (SystemState*) ((byte*)p + 0); -SystemBaseRegistry.CallOnCreate(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_CallOnCreate() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_CallOnCreate); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_CallOnDestroy(IntPtr p) - { -SystemState* v0 = (SystemState*) ((byte*)p + 0); -SystemBaseRegistry.CallOnDestroy(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_CallOnDestroy() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_CallOnDestroy); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_CallOnUpdate(IntPtr p) - { -SystemState* v0 = (SystemState*) ((byte*)p + 0); -SystemBaseRegistry.CallOnUpdate(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_CallOnUpdate() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_CallOnUpdate); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_GetDebugName(IntPtr p) - { -var v0 = default(int); -SystemBaseRegistry.GetDebugName(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_GetDebugName() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_GetDebugName); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [BurstCompile(CompileSynchronously = true)] - public static void Burst_Unity__Entities__SystemBaseRegistry_GetSystemTypeMetaIndex(IntPtr p) - { -var v0 = default(long); -SystemBaseRegistry.GetSystemTypeMetaIndex(v0); - } - [EditorBrowsable(EditorBrowsableState.Never)] - [Test] - public void BurstCompile_Unity__Entities__SystemBaseRegistry_GetSystemTypeMetaIndex() - { - BurstCompiler.CompileFunctionPointer(Burst_Unity__Entities__SystemBaseRegistry_GetSystemTypeMetaIndex); - } -} -} -#endif diff --git a/Unity.Entities.Tests/ComponentOrderVersionTests.cs b/Unity.Entities.Tests/ComponentOrderVersionTests.cs index ca9e7beb..38eaecba 100644 --- a/Unity.Entities.Tests/ComponentOrderVersionTests.cs +++ b/Unity.Entities.Tests/ComponentOrderVersionTests.cs @@ -154,14 +154,15 @@ public void SharedComponentDestroyAllButOneEntityInOddGroupEvenValuesUnchanged() } [Test] - public void UnrelatedChunkOrderUnchanged() + public void UnrelatedChunkOrderUnchanged([Values] bool immediate) { AddEvenOddTestData(); ActionEvenOdd((version, group) => { var entityType = m_Manager.GetEntityTypeHandle(); - var chunks = group.CreateArchetypeChunkArray(Allocator.TempJob); + var chunks = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); var firstEntity = chunks[0].GetNativeArray(entityType); m_Manager.DestroyEntity(firstEntity); chunks.Dispose(); diff --git a/Unity.Entities.Tests/ComponentSystemTests.cs b/Unity.Entities.Tests/ComponentSystemTests.cs index fea57a59..ffb629a6 100644 --- a/Unity.Entities.Tests/ComponentSystemTests.cs +++ b/Unity.Entities.Tests/ComponentSystemTests.cs @@ -3,9 +3,6 @@ using Unity.Burst; using Unity.Collections; using Unity.Jobs; -#if !UNITY_DOTSRUNTIME -using UnityEngine.LowLevel; -#endif using UnityEngine.Scripting; namespace Unity.Entities.Tests @@ -82,7 +79,6 @@ public void Create() Assert.IsTrue(system.Created); } -#if ENABLE_UNITY_COLLECTIONS_CHECKS class StackedTestSystem1 : TestSystem { public Type FoundTypeBefore; @@ -117,7 +113,6 @@ public void ComponentSystem_ExecutingSystemType() Assert.AreEqual(typeof(StackedTestSystem1), system1.FoundTypeAfter); } -#endif #if !UNITY_PORTABLE_TEST_RUNNER // TODO: IL2CPP_TEST_RUNNER can't handle Assert.That Throws @@ -430,19 +425,6 @@ public void CreateSystemValidParameters() #pragma warning restore 618 - class InvalidPlayerLoopSystemType - { - } - - [Test] - public void AppendToPlayerLoopSystemList_InvalidPlayerLoopSystemType_Throws() - { - var sys = World.CreateSystem(); - var playerLoop = PlayerLoop.GetCurrentPlayerLoop(); - Assert.That(() => ScriptBehaviourUpdateOrder.AppendSystemToPlayerLoopList(sys, ref playerLoop, typeof(InvalidPlayerLoopSystemType)), - Throws.ArgumentException.With.Message.Contains( - "Could not find PlayerLoopSystem with type=Unity.Entities.Tests.ComponentSystemTests+InvalidPlayerLoopSystemType")); - } #endif } } diff --git a/Unity.Entities.Tests/CreateAndDestroyTests.cs b/Unity.Entities.Tests/CreateAndDestroyTests.cs index 51b488d9..ac9c9cbf 100644 --- a/Unity.Entities.Tests/CreateAndDestroyTests.cs +++ b/Unity.Entities.Tests/CreateAndDestroyTests.cs @@ -92,6 +92,29 @@ unsafe public void CreateAndDestroyThree() AssertComponentData(entity2, 12); } + [Test] + public void CreateEntity_InvalidEntityArchetypeThrows() + { + var archetype = new EntityArchetype(); + Assert.Throws(() => m_Manager.CreateEntity(archetype)); + } + + [Test] + public void CreateEntity_PassArray_InvalidEntityArchetypeThrows() + { + var arr = new NativeArray(10, Allocator.Temp); + var archetype = new EntityArchetype(); + Assert.Throws(() => m_Manager.CreateEntity(archetype, arr)); + arr.Dispose(); + } + + [Test] + public void CreateEntity_ReturnArray_InvalidEntityArchetypeThrows() + { + var archetype = new EntityArchetype(); + Assert.Throws(() => m_Manager.CreateEntity(archetype, 10, Allocator.Temp)); + } + [Test] unsafe public void CreateAndDestroyStressTest() { @@ -192,6 +215,39 @@ public void AddRemoveComponent() m_Manager.DestroyEntity(entity); } + [Test] + public void RemoveMultipleComponents() + { + var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestData4)); + var entity = m_Manager.CreateEntity(archetype); + + m_Manager.RemoveComponent(entity, new ComponentTypes(typeof(EcsTestData2), typeof(EcsTestData4))); + Assert.AreEqual(m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData3)), m_Manager.GetChunk(entity).Archetype); + m_Manager.DestroyEntity(entity); + } + + [Test] + public void RemoveMultipleComponents_EmptyComponentTypes() + { + var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestData4)); + var entity = m_Manager.CreateEntity(archetype); + + m_Manager.RemoveComponent(entity, new ComponentTypes()); + Assert.AreEqual(archetype, m_Manager.GetChunk(entity).Archetype); + m_Manager.DestroyEntity(entity); + } + + [Test] + public void RemoveMultipleComponents_ZeroComponentsPresent() + { + var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3)); + var entity = m_Manager.CreateEntity(archetype); + + m_Manager.RemoveComponent(entity, new ComponentTypes(typeof(EcsTestTag), typeof(EcsTestData4))); + Assert.AreEqual(archetype, m_Manager.GetChunk(entity).Archetype); + m_Manager.DestroyEntity(entity); + } + [Test] public void AddComponentSetsValueOfComponentToDefault() { @@ -277,7 +333,7 @@ public void AddComponentsWorks() } [Test] - public void GetAllEntitiesCorrectCount() + public void GetAllEntitiesCorrectCount([Values] bool immediate) { var archetype0 = m_Manager.CreateArchetype(typeof(EcsTestData)); var entity = m_Manager.CreateEntity(archetype0); @@ -286,7 +342,7 @@ public void GetAllEntitiesCorrectCount() var moreEntities = new NativeArray(1024, Allocator.Temp); m_Manager.CreateEntity(archetype1, moreEntities); - var foundEntities = m_Manager.GetAllEntities(); + var foundEntities = immediate ? m_Manager.GetAllEntitiesImmediate() : m_Manager.GetAllEntities(); Assert.AreEqual(1024 + 1, foundEntities.Length); foundEntities.Dispose(); @@ -294,7 +350,7 @@ public void GetAllEntitiesCorrectCount() } [Test] - public void GetAllEntitiesCorrectValues() + public void GetAllEntitiesCorrectValues([Values] bool immediate) { var archetype0 = m_Manager.CreateArchetype(typeof(EcsTestData)); var entity = m_Manager.CreateEntity(archetype0); @@ -308,7 +364,7 @@ public void GetAllEntitiesCorrectValues() m_Manager.SetComponentData(moreEntities[i], new EcsTestData { value = i + 1}); } - var foundEntities = m_Manager.GetAllEntities(); + var foundEntities = immediate ? m_Manager.GetAllEntitiesImmediate() : m_Manager.GetAllEntities(); Assert.AreEqual(1025, foundEntities.Length); @@ -595,6 +651,14 @@ public void AddSharedComponentTwice() Assert.That(added1, Is.False); } + [Test] + public void SetArchetype_InvalidEntityArchetypeThrows() + { + var entity = m_Manager.CreateEntity(); + var archetype = new EntityArchetype(); + Assert.Throws(delegate { m_Manager.SetArchetype(entity, archetype); }); + } + [Test] public void SetArchetypeWithSharedComponentWorks() { @@ -624,6 +688,13 @@ public void SetArchetypeRemovingStateComponentThrows() Assert.Throws(() => m_Manager.SetArchetype(entity, m_Manager.CreateArchetype(typeof(EcsStateTag1)))); } + [Test] + public void SetArchetypePreservingStateComponentNoThrows() + { + var entity = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsState1)); + Assert.DoesNotThrow(() => m_Manager.SetArchetype(entity, m_Manager.CreateArchetype(typeof(EcsState1)))); + } + [Test] public void SetArchetypeAddingStateComponent() { diff --git a/Unity.Entities.Tests/DisableComponentTests.cs b/Unity.Entities.Tests/DisableComponentTests.cs index 8b429c82..cc32d521 100644 --- a/Unity.Entities.Tests/DisableComponentTests.cs +++ b/Unity.Entities.Tests/DisableComponentTests.cs @@ -89,7 +89,7 @@ public void DIS_FindDisabledIfRequestedInChunkIterator() } [Test] - public void DIS_GetAllIncludesDisabled() + public void DIS_GetAllIncludesDisabled([Values] bool immediate) { var archetype0 = m_Manager.CreateArchetype(typeof(EcsTestData)); var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(Disabled)); @@ -98,7 +98,7 @@ public void DIS_GetAllIncludesDisabled() var entity1 = m_Manager.CreateEntity(archetype1); var entity2 = m_Manager.CreateEntity(archetype1); - var entities = m_Manager.GetAllEntities(); + var entities = immediate ? m_Manager.GetAllEntitiesImmediate() : m_Manager.GetAllEntities(); Assert.AreEqual(3, entities.Length); entities.Dispose(); diff --git a/Unity.Entities.Tests/ECSTestsFixture.cs b/Unity.Entities.Tests/ECSTestsFixture.cs index 0ff4f75e..4d745850 100644 --- a/Unity.Entities.Tests/ECSTestsFixture.cs +++ b/Unity.Entities.Tests/ECSTestsFixture.cs @@ -83,7 +83,9 @@ public abstract class ECSTestsFixture public virtual void Setup() { #if !UNITY_DOTSRUNTIME + // unit tests preserve the current player loop to restore later, and start from a blank slate. m_PreviousPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); + PlayerLoop.SetPlayerLoop(PlayerLoop.GetDefaultPlayerLoop()); #endif m_PreviousWorld = World.DefaultGameObjectInjectionWorld; World = World.DefaultGameObjectInjectionWorld = new World("Test World"); diff --git a/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs b/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs new file mode 100644 index 00000000..404296a8 --- /dev/null +++ b/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs @@ -0,0 +1,47 @@ +using System; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; + +namespace Unity.Entities.Tests +{ +#if UNITY_DOTSRUNTIME + // Always ignore these tests + public class EmbeddedPackageOnlyTestAttribute : IgnoreAttribute { + public EmbeddedPackageOnlyTestAttribute() : base("Only runs in the editor when this package is embedded or referenced locally.") + { + } + } +#else + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class EmbeddedPackageOnlyTestAttribute : NUnitAttribute, IApplyToTest + { + public void ApplyToTest(Test test) + { +#if UNITY_EDITOR + var assembly = test.Method?.TypeInfo?.Assembly ?? test.TypeInfo?.Assembly; + if (assembly == null) + { + UnityEngine.Debug.LogError($"The {nameof(EmbeddedPackageOnlyTestAttribute)} attribute can only be applied to tests in an assembly."); + return; + } + + var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(assembly); + if (package == null) + { + UnityEngine.Debug.LogError( + $"The {nameof(EmbeddedPackageOnlyTestAttribute)} attribute can only be applied to tests in a package."); + return; + } + + if (package.source == UnityEditor.PackageManager.PackageSource.Embedded || + package.source == UnityEditor.PackageManager.PackageSource.Local) + return; +#endif + + test.RunState = RunState.Ignored; + test.Properties.Add(PropertyNames.SkipReason, "Only runs in the editor when this package is embedded or referenced locally."); + } + } +#endif +} diff --git a/Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs.meta b/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs.meta similarity index 100% rename from Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs.meta rename to Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs.meta diff --git a/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs b/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs new file mode 100644 index 00000000..864fe7e3 --- /dev/null +++ b/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs @@ -0,0 +1,17 @@ +#if UNITY_EDITOR +using NUnit.Framework; +using Unity.Collections.Tests; + +namespace Unity.Entities.Tests +{ + [TestFixture, EmbeddedPackageOnlyTest] + public class EntitiesBurstCompatibilityTests : BurstCompatibilityTests + { + public EntitiesBurstCompatibilityTests() + : base("Packages/com.unity.entities/Unity.Entities.Tests/_generated_burst_tests.cs", + "Unity.Entities") + { + } + } +} +#endif diff --git a/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs.meta b/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs.meta new file mode 100644 index 00000000..5876d640 --- /dev/null +++ b/Unity.Entities.Tests/EntitiesBurstCompatibilityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41303b5fb2594fa4c883696e08590044 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities.Tests/EntityCommandBufferTests.cs b/Unity.Entities.Tests/EntityCommandBufferTests.cs index 3fae01b6..7bdb30bc 100644 --- a/Unity.Entities.Tests/EntityCommandBufferTests.cs +++ b/Unity.Entities.Tests/EntityCommandBufferTests.cs @@ -371,6 +371,29 @@ public void CreateEntityWithArchetype() } } + [Test] + public void CreateEntityWithArchetype_InvalidThrows() + { + var a = new EntityArchetype(); + var cmds = new EntityCommandBuffer(Allocator.TempJob); + using (cmds) + { + Assert.Throws(() => cmds.CreateEntity(a)); + } + } + + [Test] + public void CreateEntityWithArchetype_Parallel_InvalidThrows() + { + var a = new EntityArchetype(); + var ecb = new EntityCommandBuffer(Allocator.TempJob); + var cmds = ecb.AsParallelWriter(); + using (ecb) + { + Assert.Throws(() => cmds.CreateEntity(0, a)); + } + } + [Test] public void CreateTwoComponents() { @@ -424,7 +447,7 @@ public void CreateTwoComponents() [Test] public void TestMultiChunks() { -#if UNITY_DOTSPLAYER_IL2CPP && !DEVELOP // IL2CPP is a little slow in debug; reduce the number of tests in DEBUG (but not DEVELOP). +#if UNITY_DOTSRUNTIME && !DEVELOP // IL2CPP is a little slow in debug; reduce the number of tests in DEBUG (but not DEVELOP). const int count = 4096; #else const int count = 65536; @@ -2687,6 +2710,38 @@ public void AddSharedComponent_WithEntityQuery_ThatHasNoMatch_WillNotCorruptInte m_Manager.Debug.CheckInternalConsistency(); } + [Test] + public void AddAndRemoveComponent_EntityQueryCacheIsValid() + { + var ent = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + using(EntityQuery query2 = m_Manager.CreateEntityQuery(typeof(EcsTestData), typeof(EcsTestData2), ComponentType.Exclude())) + using(EntityQuery query3 = m_Manager.CreateEntityQuery(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3))) + using (EntityCommandBuffer cmds = new EntityCommandBuffer(Allocator.TempJob)) + { + cmds.AddComponent(ent); + cmds.RemoveComponent(ent); + cmds.Playback(m_Manager); + Assert.IsFalse(query2.IsCacheValid); + Assert.IsFalse(query3.IsCacheValid); + } + } + + [Test] + public void RemoveAndAddComponent_EntityQueryCacheIsValid() + { + var ent = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + using(EntityQuery query2 = m_Manager.CreateEntityQuery(typeof(EcsTestData), typeof(EcsTestData2))) + using(EntityQuery query1 = m_Manager.CreateEntityQuery(typeof(EcsTestData), ComponentType.Exclude())) + using (EntityCommandBuffer cmds = new EntityCommandBuffer(Allocator.TempJob)) + { + cmds.RemoveComponent(ent); + cmds.AddComponent(ent); + cmds.Playback(m_Manager); + Assert.IsFalse(query2.IsCacheValid); + Assert.IsFalse(query1.IsCacheValid); + } + } + #if !UNITY_DISABLE_MANAGED_COMPONENTS private void VerifyEcsTestManagedComponent(int length, string expectedValue) { diff --git a/Unity.Entities.Tests/EntityManagerComponentGroupOperationsTests.cs b/Unity.Entities.Tests/EntityManagerComponentGroupOperationsTests.cs index 6acc9f0f..660ccb48 100644 --- a/Unity.Entities.Tests/EntityManagerComponentGroupOperationsTests.cs +++ b/Unity.Entities.Tests/EntityManagerComponentGroupOperationsTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Runtime.InteropServices; using NUnit.Framework; using Unity.Collections; @@ -126,6 +128,158 @@ public void AddRemoveAnyComponentWithGroupWorksWithVariousTypes() } } + [Test] + public void AddMultipleComponentsWithQuery() + { + var componentTypes = new ComponentTypes( + typeof(EcsTestTag), typeof(EcsTestData2), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp)); + + var entity1 = m_Manager.CreateEntity(typeof(EcsTestData)); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + var entity3 = m_Manager.CreateEntity(typeof(EcsTestData2)); + + var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + m_Manager.AddComponent(query, componentTypes); + + m_ManagerDebug.CheckInternalConsistency(); + + var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestTag), typeof(EcsTestData2), ComponentType.ChunkComponent(), + typeof(EcsTestSharedComp)); + Assert.AreEqual(archetype, m_Manager.GetChunk(entity1).Archetype); + Assert.AreEqual(archetype, m_Manager.GetChunk(entity2).Archetype); + + // verify entity3 is unchanged + Assert.AreEqual(m_Manager.CreateArchetype(typeof(EcsTestData2)), m_Manager.GetChunk(entity3).Archetype); + } + + [Test] + public void AddMultipleComponentsWithQuery_AddingEntityComponentTypeThrows() + { + var componentTypes = new ComponentTypes( + typeof(EcsTestTag), typeof(Entity), typeof(EcsTestData2), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp)); + + var entity1 = m_Manager.CreateEntity(typeof(EcsTestData)); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + var entity3 = m_Manager.CreateEntity(typeof(EcsTestData2)); + var archetype1 = m_Manager.GetChunk(entity1).Archetype; + var archetype2 = m_Manager.GetChunk(entity2).Archetype; + var archetype3 = m_Manager.GetChunk(entity3).Archetype; + + var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + Assert.Throws(() => m_Manager.AddComponent(query, componentTypes)); + + m_ManagerDebug.CheckInternalConsistency(); + + // entities should be unchanged + Assert.AreEqual(archetype1, m_Manager.GetChunk(entity1).Archetype); + Assert.AreEqual(archetype2, m_Manager.GetChunk(entity2).Archetype); + Assert.AreEqual(archetype3, m_Manager.GetChunk(entity3).Archetype); + } + + [Test] + public void AddMultipleComponentsWithQuery_ExceedMaxSharedComponentsThrows() + { + var componentTypes = new ComponentTypes(new ComponentType[] { + typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2), typeof(EcsTestSharedComp3), typeof(EcsTestSharedComp4), + typeof(EcsTestSharedComp5), typeof(EcsTestSharedComp6), typeof(EcsTestSharedComp7), typeof(EcsTestSharedComp8) + }); + + Assert.AreEqual(8, EntityComponentStore.kMaxSharedComponentCount); // if kMaxSharedComponentCount changes, need to update this test + + var entity1 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestSharedComp9)); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + var entity3 = m_Manager.CreateEntity(typeof(EcsTestData2)); + var archetype1 = m_Manager.GetChunk(entity1).Archetype; + var archetype2 = m_Manager.GetChunk(entity2).Archetype; + var archetype3 = m_Manager.GetChunk(entity3).Archetype; + + var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + +#if UNITY_DOTSRUNTIME + Assert.Throws(() => m_Manager.AddComponent(query, componentTypes)); +#else + Assert.That(() => m_Manager.AddComponent(query, componentTypes), Throws.InvalidOperationException + .With.Message.EqualTo("Cannot add more than 8 SharedComponent to a single Archetype")); +#endif + + m_ManagerDebug.CheckInternalConsistency(); + + // entities should be unchanged + Assert.AreEqual(archetype1, m_Manager.GetChunk(entity1).Archetype); + Assert.AreEqual(archetype2, m_Manager.GetChunk(entity2).Archetype); + Assert.AreEqual(archetype3, m_Manager.GetChunk(entity3).Archetype); + } + + private struct EcsTestDataHuge : IComponentData + { + public FixedString4096 value0; + public FixedString4096 value1; + public FixedString4096 value2; + public FixedString4096 value3; + public FixedString4096 value4; + + public EcsTestDataHuge(FixedString4096 inValue) + { + value4 = value3 = value2 = value1 = value0 = inValue; + } + } + + [Test] + public void AddMultipleComponentsWithQuery_ExceedChunkCapacityThrows() + { + var componentTypes = new ComponentTypes(typeof(EcsTestDataHuge)); // add really big component(s) + + Assert.AreEqual(16064, Chunk.GetChunkBufferSize()); // if chunk size changes, need to update this test + + var entity1 = m_Manager.CreateEntity(typeof(EcsTestData)); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); + var entity3 = m_Manager.CreateEntity(typeof(EcsTestData2)); + var archetype1 = m_Manager.GetChunk(entity1).Archetype; + var archetype2 = m_Manager.GetChunk(entity2).Archetype; + var archetype3 = m_Manager.GetChunk(entity3).Archetype; + + var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + +#if UNITY_DOTSRUNTIME + Assert.Throws(() => m_Manager.AddComponent(query, componentTypes)); +#else + Assert.That(() => m_Manager.AddComponent(query, componentTypes), Throws.InvalidOperationException + .With.Message.Contains("Entity archetype component data is too large.")); +#endif + + m_ManagerDebug.CheckInternalConsistency(); + + // entities should be unchanged + Assert.AreEqual(archetype1, m_Manager.GetChunk(entity1).Archetype); + Assert.AreEqual(archetype2, m_Manager.GetChunk(entity2).Archetype); + Assert.AreEqual(archetype3, m_Manager.GetChunk(entity3).Archetype); + } + + [Test] + [Ignore("Changes this test was introduced with were reverted due to a bug")] + public void RemoveMultipleComponentsWithQuery() + { + var entity1 = m_Manager.CreateEntity(typeof(EcsTestTag), typeof(EcsTestData), typeof(EcsTestData4), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp) ); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData4), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp) ); + var entity3 = m_Manager.CreateEntity(typeof(EcsTestData2)); + + var query = m_Manager.CreateEntityQuery(typeof(EcsTestData2)); + + m_Manager.RemoveComponent(query, new ComponentTypes(typeof(EcsTestData2), typeof(EcsTestData4), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp))); + + m_ManagerDebug.CheckInternalConsistency(); + + var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestTag), typeof(EcsTestData), typeof(EcsTestData4), ComponentType.ChunkComponent(), typeof(EcsTestSharedComp) ); + var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData)); + var archetype3 = m_Manager.CreateArchetype(); + Assert.AreEqual(archetype1, m_Manager.GetChunk(entity1).Archetype); + Assert.AreEqual(archetype2, m_Manager.GetChunk(entity2).Archetype); + Assert.AreEqual(archetype3, m_Manager.GetChunk(entity3).Archetype); + } + [Test] [IgnoreInPortableTests("intermittent crash (likely race condition)")] public void RemoveAnyComponentWithGroupIgnoresChunksThatDontHaveTheComponent() diff --git a/Unity.Entities.Tests/EntityManagerUnmanagedTests.cs b/Unity.Entities.Tests/EntityManagerUnmanagedTests.cs index 6ec6ee8d..ebee5fdb 100644 --- a/Unity.Entities.Tests/EntityManagerUnmanagedTests.cs +++ b/Unity.Entities.Tests/EntityManagerUnmanagedTests.cs @@ -40,6 +40,7 @@ public void OnDestroy(ref SystemState state) { } + [BurstCompile(CompileSynchronously = true)] public void OnUpdate(ref SystemState state) { diff --git a/Unity.Entities.Tests/EntityQueryTests.cs b/Unity.Entities.Tests/EntityQueryTests.cs index 410d9b83..9a3b386b 100644 --- a/Unity.Entities.Tests/EntityQueryTests.cs +++ b/Unity.Entities.Tests/EntityQueryTests.cs @@ -72,7 +72,7 @@ void SetShared(Entity e, int i) } [Test] - public void CreateArchetypeChunkArray_FiltersSharedComponents() + public void CreateArchetypeChunkArray_FiltersSharedComponents([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData2), typeof(EcsTestSharedComp)); @@ -86,11 +86,13 @@ public void CreateArchetypeChunkArray_FiltersSharedComponents() group.SetSharedComponentFilter(new EcsTestSharedComp(1)); - var queriedChunks1 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetSharedComponentFilter(new EcsTestSharedComp(2)); - var queriedChunks2 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1.Concat(createdChunks2), queriedChunks1); CollectionAssert.AreEquivalent(createdChunks3.Concat(createdChunks4), queriedChunks2); @@ -107,7 +109,7 @@ void SetShared(Entity e, int i, int j) } [Test] - public void CreateArchetypeChunkArray_FiltersTwoSharedComponents() + public void CreateArchetypeChunkArray_FiltersTwoSharedComponents([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData2), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); @@ -124,16 +126,20 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedComponents() var group = m_Manager.CreateEntityQuery(typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); group.SetSharedComponentFilter(new EcsTestSharedComp(1), new EcsTestSharedComp2(7)); - var queriedChunks1 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetSharedComponentFilter(new EcsTestSharedComp(2), new EcsTestSharedComp2(7)); - var queriedChunks2 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetSharedComponentFilter(new EcsTestSharedComp(1), new EcsTestSharedComp2(8)); - var queriedChunks3 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetSharedComponentFilter(new EcsTestSharedComp(2), new EcsTestSharedComp2(8)); - var queriedChunks4 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1.Concat(createdChunks2), queriedChunks1); @@ -154,7 +160,7 @@ void SetData(Entity e, int i) } [Test] - public void CreateArchetypeChunkArray_FiltersChangeVersions() + public void CreateArchetypeChunkArray_FiltersChangeVersions([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); @@ -172,16 +178,20 @@ public void CreateArchetypeChunkArray_FiltersChangeVersions() group.SetChangedVersionFilter(typeof(EcsTestData)); group.SetChangedFilterRequiredVersion(10); - var queriedChunks1 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetChangedFilterRequiredVersion(20); - var queriedChunks2 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetChangedFilterRequiredVersion(30); - var queriedChunks3 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); group.SetChangedFilterRequiredVersion(40); - var queriedChunks4 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1.Concat(createdChunks2).Concat(createdChunks3), queriedChunks1); CollectionAssert.AreEquivalent(createdChunks2.Concat(createdChunks3), queriedChunks2); @@ -203,7 +213,7 @@ void SetData(Entity e, int i, int j) } [Test] - public void CreateArchetypeChunkArray_FiltersTwoChangeVersions() + public void CreateArchetypeChunkArray_FiltersTwoChangeVersions([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3)); @@ -225,7 +235,8 @@ public void CreateArchetypeChunkArray_FiltersTwoChangeVersions() var testType1 = m_Manager.GetComponentTypeHandle(false); var testType2 = m_Manager.GetComponentTypeHandle(false); - var queriedChunks1 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); foreach (var chunk in createdChunks1) { @@ -233,7 +244,8 @@ public void CreateArchetypeChunkArray_FiltersTwoChangeVersions() array[0] = new EcsTestData(7); } - var queriedChunks2 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); foreach (var chunk in createdChunks2) { @@ -241,7 +253,8 @@ public void CreateArchetypeChunkArray_FiltersTwoChangeVersions() array[0] = new EcsTestData2(7); } - var queriedChunks3 = group.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks3, queriedChunks1); @@ -260,7 +273,7 @@ void SetDataAndShared(Entity e, int data, int shared) } [Test] - public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion() + public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp)); @@ -280,10 +293,12 @@ public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion() query.AddChangedVersionFilter(typeof(EcsTestData)); query.AddSharedComponentFilter(new EcsTestSharedComp {value = 1}); - var queriedChunks1 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) : + query.CreateArchetypeChunkArray(Allocator.TempJob); query.SetChangedFilterRequiredVersion(10); - var queriedChunks2 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) : + query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData1 for createdChunks1 m_ManagerDebug.SetGlobalSystemVersion(20); @@ -292,7 +307,8 @@ public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData {value = 10}; } - var queriedChunks3 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) : + query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData2 query.SetChangedFilterRequiredVersion(20); @@ -302,7 +318,8 @@ public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData2 {value1 = 10, value0 = 10}; } - var queriedChunks4 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) : + query.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1.Concat(createdChunks2), queriedChunks1); // query 1 = created 1,2 Assert.AreEqual(0, queriedChunks2.Length); // query 2 is empty @@ -317,7 +334,7 @@ public void CreateArchetypeChunkArray_FiltersOneSharedOneChangeVersion() } [Test] - public void CreateArchetypeChunkArray_FiltersOneSharedTwoChangeVersion() + public void CreateArchetypeChunkArray_FiltersOneSharedTwoChangeVersion([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestSharedComp)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp)); @@ -338,10 +355,12 @@ public void CreateArchetypeChunkArray_FiltersOneSharedTwoChangeVersion() query.AddChangedVersionFilter(typeof(EcsTestData2)); query.AddSharedComponentFilter(new EcsTestSharedComp {value = 1}); - var queriedChunks1 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); query.SetChangedFilterRequiredVersion(10); - var queriedChunks2 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData1 for createdChunks1 m_ManagerDebug.SetGlobalSystemVersion(20); @@ -350,7 +369,8 @@ public void CreateArchetypeChunkArray_FiltersOneSharedTwoChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData {value = 10}; } - var queriedChunks3 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData2 query.SetChangedFilterRequiredVersion(20); @@ -360,7 +380,8 @@ public void CreateArchetypeChunkArray_FiltersOneSharedTwoChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData2 {value1 = 10, value0 = 10}; } - var queriedChunks4 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1.Concat(createdChunks2), queriedChunks1); // query 1 = created 1,2 Assert.AreEqual(0, queriedChunks2.Length); // query 2 is empty @@ -381,7 +402,7 @@ void SetDataAndShared(Entity e, int data, int shared1, int shared2) } [Test] - public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion() + public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); @@ -402,10 +423,12 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion() query.AddSharedComponentFilter(new EcsTestSharedComp {value = 1}); query.AddSharedComponentFilter(new EcsTestSharedComp2 {value0 = 3, value1 = 3}); - var queriedChunks1 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); query.SetChangedFilterRequiredVersion(10); - var queriedChunks2 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData1 for createdChunks1 and createdChunks2 m_ManagerDebug.SetGlobalSystemVersion(20); @@ -420,7 +443,8 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion() array[0] = new EcsTestData {value = 10}; } } - var queriedChunks3 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData2 for createdChunks1 query.SetChangedFilterRequiredVersion(20); @@ -430,7 +454,8 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData2 {value1 = 10, value0 = 10}; } - var queriedChunks4 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1, queriedChunks1); // query 1 = created 1 Assert.AreEqual(0, queriedChunks2.Length); // query 2 is empty @@ -445,7 +470,7 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedOneChangeVersion() } [Test] - public void CreateArchetypeChunkArray_FiltersTwoSharedTwoChangeVersion() + public void CreateArchetypeChunkArray_FiltersTwoSharedTwoChangeVersion([Values] bool immediate) { var archetype1 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); var archetype2 = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp), typeof(EcsTestSharedComp2)); @@ -467,10 +492,12 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedTwoChangeVersion() query.AddSharedComponentFilter(new EcsTestSharedComp {value = 1}); query.AddSharedComponentFilter(new EcsTestSharedComp2 {value0 = 3, value1 = 3}); - var queriedChunks1 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks1 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); query.SetChangedFilterRequiredVersion(10); - var queriedChunks2 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks2 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData1 for createdChunks1 and createdChunks2 m_ManagerDebug.SetGlobalSystemVersion(20); @@ -485,7 +512,8 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedTwoChangeVersion() array[0] = new EcsTestData {value = 10}; } } - var queriedChunks3 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks3 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); // bumps the version number for TestData2 for createdChunks1 query.SetChangedFilterRequiredVersion(20); @@ -495,7 +523,8 @@ public void CreateArchetypeChunkArray_FiltersTwoSharedTwoChangeVersion() var array = createdChunks1[i].GetNativeArray(EmptySystem.GetComponentTypeHandle()); array[0] = new EcsTestData2 {value1 = 10, value0 = 10}; } - var queriedChunks4 = query.CreateArchetypeChunkArray(Allocator.TempJob); + var queriedChunks4 = immediate ? query.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : query.CreateArchetypeChunkArray(Allocator.TempJob); CollectionAssert.AreEquivalent(createdChunks1, queriedChunks1); // query 1 = created 1 Assert.AreEqual(0, queriedChunks2.Length); // query 2 is empty @@ -549,7 +578,7 @@ protected override JobHandle OnUpdate(JobHandle input) } [Test] - public unsafe void CreateArchetypeChunkArray_SyncsChangeFilterTypes() + public unsafe void CreateArchetypeChunkArray_SyncsChangeFilterTypes([Values] bool immediate) { var group = m_Manager.CreateEntityQuery(typeof(EcsTestData)); group.SetChangedVersionFilter(typeof(EcsTestData)); @@ -558,7 +587,8 @@ public unsafe void CreateArchetypeChunkArray_SyncsChangeFilterTypes() var safetyHandle = m_Manager.GetCheckedEntityDataAccess()->DependencyManager->Safety.GetSafetyHandle(TypeManager.GetTypeIndex(), false); Assert.Throws(() => AtomicSafetyHandle.CheckWriteAndThrow(safetyHandle)); - var chunks = group.CreateArchetypeChunkArray(Allocator.TempJob); + var chunks = immediate ? group.CreateArchetypeChunkArrayImmediate(Allocator.TempJob) + : group.CreateArchetypeChunkArray(Allocator.TempJob); AtomicSafetyHandle.CheckWriteAndThrow(safetyHandle); chunks.Dispose(); @@ -802,6 +832,225 @@ public void MatchesArchetypeAddedAfterMaskCreation() Assert.True(queryMask.Matches(entity)); } + [Test] + public void ChunkListCaching_CreateEntity() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entitiesA = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_CreateEntity_NoNewChunk() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entityA = m_Manager.CreateEntity(archetypeA); + + // Update the cache + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + // no new chunk of matching archetype + var entityB = m_Manager.CreateEntity(archetypeA); + + // cache should still be valid + Assert.IsTrue(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_DestroyEntity() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entitiesA = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + var entitiesB = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + // destroy entities + m_Manager.DestroyEntity(entitiesA); + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_AddComponent() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entities = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + for (int i = 0; i < entities.Length; ++i) + { + m_Manager.AddComponent(entities[i], ComponentType.ReadWrite()); + } + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_RemoveComponent() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData), ComponentType.ReadWrite()); + + var queryA = m_Manager.CreateEntityQuery( new EntityQueryDesc + { + All = new[]{ComponentType.ReadWrite()}, + None = new[]{ComponentType.ReadWrite()} + }); + + var entities = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + for (int i = 0; i < entities.Length; ++i) + { + m_Manager.RemoveComponent(entities[i], ComponentType.ReadWrite()); + } + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_MoveEntitiesFrom() + { + // move from another world + using(var newWorld = new World("testworld")) + { + var archetype = newWorld.EntityManager.CreateArchetype(typeof(EcsTestData3)); + newWorld.EntityManager.CreateEntity(archetype); + + newWorld.EntityManager.UniversalQuery.UpdateCache(); + Assert.IsTrue(newWorld.EntityManager.UniversalQuery.IsCacheValid); + + m_Manager.MoveEntitiesFrom(newWorld.EntityManager); + + Assert.IsFalse(newWorld.EntityManager.UniversalQuery.IsCacheValid); + } + + var queryC = m_Manager.CreateEntityQuery(typeof(EcsTestData3)); + Assert.IsFalse(queryC.IsCacheValid); + } + + [Test] + public void ChunkListCaching_CopyAndReplaceEntitiesFrom() + { + var queryC = m_Manager.CreateEntityQuery(typeof(EcsTestData3)); + queryC.UpdateCache(); + Assert.IsTrue(queryC.IsCacheValid); + + // move from another world + using(var newWorld = new World("testworld")) + { + var archetype = newWorld.EntityManager.CreateArchetype(typeof(EcsTestData3)); + newWorld.EntityManager.CreateEntity(archetype); + + newWorld.EntityManager.UniversalQuery.UpdateCache(); + Assert.IsTrue(newWorld.EntityManager.UniversalQuery.IsCacheValid); + + m_Manager.CopyAndReplaceEntitiesFrom(newWorld.EntityManager); + + Assert.IsTrue(newWorld.EntityManager.UniversalQuery.IsCacheValid); + } + + Assert.IsFalse(queryC.IsCacheValid); + } + + [Test] + public void ChunkListCaching_Instantiate() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entityA = m_Manager.CreateEntity(archetypeA); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + m_Manager.Instantiate(entityA, archetypeA.ChunkCapacity, Allocator.Temp); + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_AddSharedComponent() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entities = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + for (int i = 0; i < entities.Length; ++i) + { + m_Manager.AddSharedComponentData(entities[i], new EcsTestSharedComp{value = 10}); + } + + Assert.IsFalse(queryA.IsCacheValid); + } + + [Test] + public void ChunkListCaching_SetSharedComponent() + { + // new archetype, no query + var archetypeA = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp)); + + // new query, exisiting archetype, no entities + var queryA = m_Manager.CreateEntityQuery(typeof(EcsTestData)); + + // new chunk of matching archetype + var entities = m_Manager.CreateEntity(archetypeA, archetypeA.ChunkCapacity, Allocator.Temp); + + queryA.UpdateCache(); + Assert.IsTrue(queryA.IsCacheValid); + + for (int i = 0; i < entities.Length; ++i) + { + m_Manager.SetSharedComponentData(entities[i], new EcsTestSharedComp{value = 10}); + } + + Assert.IsFalse(queryA.IsCacheValid); + } + #if !UNITY_DOTSRUNTIME // IJobForEach is deprecated [AlwaysUpdateSystem] public class CachedSystemQueryTestSystem : JobComponentSystem diff --git a/Unity.Entities.Tests/IJobChunkTests.cs b/Unity.Entities.Tests/IJobChunkTests.cs index 2fd1b276..0542458a 100644 --- a/Unity.Entities.Tests/IJobChunkTests.cs +++ b/Unity.Entities.Tests/IJobChunkTests.cs @@ -390,6 +390,7 @@ public void FilteredIJobChunkProcessesSameChunksAsFilteredJobForEach() #endif // !UNITY_DOTSRUNTIME +#if !UNITY_2020_2_OR_NEWER struct InitializedAsSingleAndParallelJob : IJobChunk { public void Execute(ArchetypeChunk chunk, int chunkIndex, int entityOffset) {} @@ -402,5 +403,6 @@ public void IJobChunkInitializedAsSingleAndParallel_CreatesDifferentScheduleData Assert.AreNotEqual(jobReflectionDataParallel, jobReflectionDataSingle); } +#endif } } diff --git a/Unity.Entities.Tests/IJobEntityBatchTests.cs b/Unity.Entities.Tests/IJobEntityBatchTests.cs index 15483989..2fd5d86e 100644 --- a/Unity.Entities.Tests/IJobEntityBatchTests.cs +++ b/Unity.Entities.Tests/IJobEntityBatchTests.cs @@ -32,7 +32,7 @@ public void IJobEntityBatchProcess([Values(1, 4, 17, 100)] int jobsPerChunk) { EcsTestTypeHandle = m_Manager.GetComponentTypeHandle(false) }; - job.ScheduleParallelBatched(query, jobsPerChunk).Complete(); + job.ScheduleParallel(query, jobsPerChunk).Complete(); for (int batchIndex = 0; batchIndex < jobsPerChunk; ++batchIndex) { @@ -72,7 +72,7 @@ public void IJobEntityBatchWithIndex([Values(1, 4, 17, 100)] int jobsPerChunk) { EcsTestTypeHandle = m_Manager.GetComponentTypeHandle(false) }; - job.ScheduleParallelBatched(query, jobsPerChunk).Complete(); + job.ScheduleParallel(query, jobsPerChunk).Complete(); for (int batchIndex = 0; batchIndex < jobsPerChunk; ++batchIndex) { diff --git a/Unity.Entities.Tests/JobBasicTests.cs b/Unity.Entities.Tests/JobBasicTests.cs index 37ae7670..36b1bccb 100644 --- a/Unity.Entities.Tests/JobBasicTests.cs +++ b/Unity.Entities.Tests/JobBasicTests.cs @@ -4,6 +4,7 @@ using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; +using Unity.Entities; using Unity.Jobs; using Unity.Jobs.LowLevel.Unsafe; @@ -652,5 +653,50 @@ public void TestSimpleIJobChunk([Values(0, 1, 2, 3)] int mode, [Values(1, 100)] listOfInt.Dispose(); eArr.Dispose(); } + + public struct SimpleJobFor : IJobFor + { + [WriteOnly] + public NativeHashMap.ParallelWriter result; + + public void Execute(int i) + { + result.TryAdd(i, 123); + } + } + + [Test] + public void TestIJobFor([Values(0, 1, 2)] int mode) + { + const int N = 1000; + + NativeHashMap output = new NativeHashMap(N, Allocator.TempJob); + SimpleJobFor job = new SimpleJobFor() + { + result = output.AsParallelWriter() + }; + + if (mode == 0) + { + job.Run(N); + } + else if (mode == 1) + { + job.Schedule(N, new JobHandle()).Complete(); + } + else + { + job.ScheduleParallel(N, 13, new JobHandle()).Complete(); + } + + Assert.AreEqual(N, output.Count()); + for (int i = 0; i < N; ++i) + { + Assert.AreEqual(123, output[i]); + } + + output.Dispose(); + } + } } diff --git a/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs b/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs index 8727c4dc..e121aedc 100644 --- a/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs +++ b/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs @@ -28,6 +28,20 @@ public void GetSetSingleton() Assert.AreEqual(10, GetSingleton().value); } + T GenericMethodWithSingletonAccess(T value) where T : struct, IComponentData + { + SetSingleton(value); + return GetSingleton(); + } + + public void GetSetSingletonWithGenericParameter() + { + EntityManager.CreateEntity(typeof(EcsTestData)); + + GenericMethodWithSingletonAccess(new EcsTestData(10)); + Assert.AreEqual(10, GetSingleton().value); + } + public void SingletonMethodsWithValidFilter_GetsAndSets() { var queryWithFilter1 = EntityManager.CreateEntityQuery(typeof(EcsTestData), typeof(SharedData1)); @@ -196,6 +210,12 @@ public void SystemBase_GetSetSingleton() TestSystem.GetSetSingleton(); } + [Test] + public void SystemBase_GetSetSingletonWithGenericParameter() + { + TestSystem.GetSetSingletonWithGenericParameter(); + } + [Test] public void SystemBase_SingletonMethodsWithValidFilter_GetsAndSets() { diff --git a/Unity.Entities.Tests/TestComponents.cs b/Unity.Entities.Tests/TestComponents.cs index 56431f0f..c63e0a5c 100644 --- a/Unity.Entities.Tests/TestComponents.cs +++ b/Unity.Entities.Tests/TestComponents.cs @@ -4,6 +4,7 @@ using Unity.Entities; using Unity.Entities.Tests; using Unity.Assertions; +using Unity.Collections; [assembly: RegisterGenericComponentType(typeof(EcsTestGeneric))] [assembly: RegisterGenericComponentType(typeof(EcsTestGeneric))] @@ -143,6 +144,79 @@ public EcsTestSharedComp3(int inValue) } } + // need many shared types for testing that we don't exceed kMaxNumSharedComponentCount + public struct EcsTestSharedComp4 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp4(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + + public struct EcsTestSharedComp5 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp5(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + + public struct EcsTestSharedComp6 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp6(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + + public struct EcsTestSharedComp7 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp7(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + + public struct EcsTestSharedComp8 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp8(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + + public struct EcsTestSharedComp9 : ISharedComponentData + { + public int value0; + public int value1; + public int value2; + + public EcsTestSharedComp9(int inValue) + { + value0 = value1 = value2 = inValue; + } + } + [MaximumChunkCapacity(475)] struct EcsTestSharedCompWithMaxChunkCapacity : ISharedComponentData { diff --git a/Unity.Entities.Tests/Unity.Entities.Tests.asmdef b/Unity.Entities.Tests/Unity.Entities.Tests.asmdef index cf69b09e..ae56233a 100644 --- a/Unity.Entities.Tests/Unity.Entities.Tests.asmdef +++ b/Unity.Entities.Tests/Unity.Entities.Tests.asmdef @@ -6,10 +6,11 @@ "Unity.Jobs", "Unity.Burst", "Unity.Mathematics", - "Unity.Transforms" + "Unity.Transforms", + "Unity.Collections.BurstCompatibilityGen" ], "optionalUnityReferences": [ - "TestAssemblies" + "TestAssemblies" ], "includePlatforms": [ "Editor" @@ -20,5 +21,6 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [] + "versionDefines": [], + "noEngineReferences": false } diff --git a/Unity.Entities.Tests/WorldTests.cs b/Unity.Entities.Tests/WorldTests.cs index 0d5d4b44..cd18b104 100644 --- a/Unity.Entities.Tests/WorldTests.cs +++ b/Unity.Entities.Tests/WorldTests.cs @@ -8,9 +8,6 @@ using Unity.Jobs; using Unity.Mathematics; using System.Diagnostics; -#if !UNITY_DOTSRUNTIME -using UnityEngine.LowLevel; -#endif #if !UNITY_PORTABLE_TEST_RUNNER using System.Reflection; using System.Linq; @@ -352,95 +349,6 @@ void AdvanceWorldTime(float amount) } } -#if !UNITY_DOTSRUNTIME - // Player loop manipulation tests - [Test] - public void IsInPlayerLoop_WorldNotInPlayerLoop_ReturnsFalse() - { - using (var world = new World("Test World")) - { - world.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); - } - } - - [Test] - public void IsInPlayerLoop_WorldInPlayerLoop_ReturnsTrue() - { - using (var world = new World("Test World")) - { - world.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref playerLoop); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); - } - } - - [Test] - public void RemoveFromPlayerLoop_WorldNotInPlayerLoop_DoesntThrow() - { - using (var world = new World("Test World")) - { - world.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(world, ref playerLoop); - Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); - } - } - - [Test] - public void RemoveFromPlayerLoop_WorldInPlayerLoop_Works() - { - using (var world = new World("Test World")) - { - world.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref playerLoop); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); - ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(world, ref playerLoop); - Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(world, playerLoop)); - } - } - - [Test] - public void AddToPlayerLoop_AddTwoWorlds_BothAreAdded() - { - using (var worldA = new World("Test World A")) - using (var worldB = new World("Test World B")) - { - worldA.CreateSystem(); - worldB.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldA, ref playerLoop); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldB, ref playerLoop); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); - } - } - - [Test] - public void RemoveFromPlayerLoop_OtherWorldsInPlayerLoop_NotAffected() - { - using (var worldA = new World("Test World A")) - using (var worldB = new World("Test World B")) - { - worldA.CreateSystem(); - worldB.CreateSystem(); - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldA, ref playerLoop); - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(worldB, ref playerLoop); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); - - ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(worldA, ref playerLoop); - Assert.IsFalse(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldA, playerLoop)); - Assert.IsTrue(ScriptBehaviourUpdateOrder.IsWorldInPlayerLoop(worldB, playerLoop)); - } - } -#endif - #if !UNITY_PORTABLE_TEST_RUNNER // https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] diff --git a/Unity.Entities/BurstCompatibleAttribute.cs b/Unity.Entities/BurstCompatibleAttribute.cs deleted file mode 100644 index 463c568a..00000000 --- a/Unity.Entities/BurstCompatibleAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Unity.Entities -{ - /// - /// Internal attribute to document and enforce that the tagged method or property has to stay burst compatible. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property)] - public class BurstCompatibleAttribute : Attribute - { - } - - /// - /// Internal attribute to state that a method is not burst compatible even though the containing type is. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] - public class NotBurstCompatibleAttribute : Attribute - { - } -} diff --git a/Unity.Entities/BurstCompatibleAttribute.cs.meta b/Unity.Entities/BurstCompatibleAttribute.cs.meta deleted file mode 100644 index 99b0697d..00000000 --- a/Unity.Entities/BurstCompatibleAttribute.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 83e89a0b73b7466d99042a9244267ad8 -timeCreated: 1587575616 \ No newline at end of file diff --git a/Unity.Entities/ChunkDataUtility.cs b/Unity.Entities/ChunkDataUtility.cs index e31c1fa0..f3420f97 100644 --- a/Unity.Entities/ChunkDataUtility.cs +++ b/Unity.Entities/ChunkDataUtility.cs @@ -502,7 +502,7 @@ static void ReleaseChunk(Chunk* chunk) if (chunk->Count < chunk->Capacity) chunk->Archetype->EmptySlotTrackingRemoveChunk(chunk); - chunk->Archetype->RemoveFromChunkList(chunk); + chunk->Archetype->RemoveFromChunkList(chunk, ref entityComponentStore->m_ChunkListChangesTracker); chunk->Archetype = null; entityComponentStore->FreeChunk(chunk); @@ -691,7 +691,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety int chunkIndexInSrcArchetype = srcChunk->ListIndex; //Change version is overriden below - dstArchetype->AddToChunkList(srcChunk, sharedComponentValues, 0); + dstArchetype->AddToChunkList(srcChunk, sharedComponentValues, 0, ref entityComponentStore->m_ChunkListChangesTracker); int chunkIndexInDstArchetype = srcChunk->ListIndex; // For unchanged components: Copy versions from src to dst archetype @@ -702,7 +702,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety CloneChangeVersions(srcArchetype, chunkIndexInSrcArchetype, dstArchetype, chunkIndexInDstArchetype); srcChunk->ListIndex = chunkIndexInSrcArchetype; - srcArchetype->RemoveFromChunkList(srcChunk); + srcArchetype->RemoveFromChunkList(srcChunk, ref entityComponentStore->m_ChunkListChangesTracker); srcChunk->ListIndex = chunkIndexInDstArchetype; if (hasEmptySlots) @@ -740,13 +740,13 @@ public static void MoveArchetype(Chunk* chunk, Archetype* dstArchetype, SharedCo if (chunk->Count < chunk->Capacity) srcArchetype->EmptySlotTrackingRemoveChunk(chunk); - srcArchetype->RemoveFromChunkList(chunk); + srcArchetype->RemoveFromChunkList(chunk, ref entityComponentStore->m_ChunkListChangesTracker); srcArchetype->EntityCount -= chunk->Count; chunk->Archetype = dstArchetype; dstArchetype->EntityCount += chunk->Count; - dstArchetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion); + dstArchetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); if (chunk->Count < chunk->Capacity) dstArchetype->EmptySlotTrackingAddChunk(chunk); @@ -1130,7 +1130,7 @@ public static void AddExistingChunk(Chunk* chunk, int* sharedComponentIndices) var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - archetype->AddToChunkList(chunk, sharedComponentIndices, globalSystemVersion); + archetype->AddToChunkList(chunk, sharedComponentIndices, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); archetype->EntityCount += chunk->Count; for (var i = 0; i < archetype->NumSharedComponents; ++i) @@ -1164,7 +1164,7 @@ public static void AddEmptyChunk(Archetype* archetype, Chunk* chunk, SharedCompo } } - archetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion); + archetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); Assert.IsTrue(archetype->Chunks.Count != 0); diff --git a/Unity.Entities/ComponentSystem.cs b/Unity.Entities/ComponentSystem.cs index 5541b5be..94a07127 100644 --- a/Unity.Entities/ComponentSystem.cs +++ b/Unity.Entities/ComponentSystem.cs @@ -99,10 +99,8 @@ public sealed override void Update() BeforeOnUpdate(); - #if ENABLE_UNITY_COLLECTIONS_CHECKS var oldExecutingSystem = ms_ExecutingSystem; ms_ExecutingSystem = this; - #endif try { @@ -110,9 +108,7 @@ public sealed override void Update() } finally { - #if ENABLE_UNITY_COLLECTIONS_CHECKS ms_ExecutingSystem = oldExecutingSystem; - #endif AfterOnUpdate(); } } diff --git a/Unity.Entities/ComponentSystemBase.cs b/Unity.Entities/ComponentSystemBase.cs index 7e31f68d..a05b5a8f 100644 --- a/Unity.Entities/ComponentSystemBase.cs +++ b/Unity.Entities/ComponentSystemBase.cs @@ -7,311 +7,10 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Core; -using Unity.Jobs; -using Unity.Jobs.LowLevel.Unsafe; +using UnityEngine.Profiling; namespace Unity.Entities { - public unsafe struct SystemState - { - internal int m_UnmanagedMetaIndex; - internal void* m_SystemPtr; - - private UnsafeList m_EntityQueries; - private UnsafeList m_RequiredEntityQueries; - - internal ref UnsafeList EntityQueries - { - get - { - fixed(void* ptr = &m_EntityQueries) - { - return ref UnsafeUtility.AsRef>(ptr); - } - } - } - - internal ref UnsafeList RequiredEntityQueries - { - get - { - fixed(void* ptr = &m_RequiredEntityQueries) - { - return ref UnsafeUtility.AsRef>(ptr); - } - } - } - - internal UnsafeIntList m_JobDependencyForReadingSystems; - internal UnsafeIntList m_JobDependencyForWritingSystems; - - internal uint m_LastSystemVersion; - - internal EntityManager m_EntityManager; - internal EntityComponentStore* m_EntityComponentStore; - internal ComponentDependencyManager* m_DependencyManager; - internal GCHandle m_World; - - internal bool m_AlwaysUpdateSystem; - internal bool m_Enabled; - internal bool m_PreviouslyEnabled; - - // SystemBase stuff - internal bool m_GetDependencyFromSafetyManager; - internal JobHandle m_JobHandle; - - #if ENABLE_PROFILER - internal Profiling.ProfilerMarker m_ProfilerMarker; - #endif - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - internal int m_SystemID; -#endif - - public bool Enabled { get => m_Enabled; set => m_Enabled = value; } - public uint GlobalSystemVersion => m_EntityComponentStore->GlobalSystemVersion; - public uint LastSystemVersion => m_LastSystemVersion; - public EntityManager EntityManager => m_EntityManager; - - public World World => (World)m_World.Target; - public ref readonly TimeData Time => ref World.Time; - - internal static SystemState* Allocate() - { - void* result = UnsafeUtility.Malloc(sizeof(SystemState), 16, Allocator.Persistent); - UnsafeUtility.MemClear(result, sizeof(SystemState)); - return (SystemState*)result; - } - - internal static void Deallocate(SystemState* state) - { - UnsafeUtility.Free(state, Allocator.Persistent); - } - - // Apply changes on top of zero-initialized heap allocation - internal void Init(World world, Type managedType) - { - m_Enabled = true; - m_UnmanagedMetaIndex = -1; - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - m_SystemID = World.AllocateSystemID(); -#endif - m_World = GCHandle.Alloc(world); - m_EntityManager = world.EntityManager; - m_EntityComponentStore = m_EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; - m_DependencyManager = m_EntityManager.GetCheckedEntityDataAccess()->DependencyManager; - - EntityQueries = new UnsafeList(0, Allocator.Persistent); - RequiredEntityQueries = new UnsafeList(0, Allocator.Persistent); - - m_JobDependencyForReadingSystems = new UnsafeIntList(0, Allocator.Persistent); - m_JobDependencyForWritingSystems = new UnsafeIntList(0, Allocator.Persistent); - - m_AlwaysUpdateSystem = false; - - if (managedType != null) - { -#if !UNITY_DOTSRUNTIME - m_AlwaysUpdateSystem = Attribute.IsDefined(managedType, typeof(AlwaysUpdateSystemAttribute), true); -#else - var attrs = TypeManager.GetSystemAttributes(managedType, typeof(AlwaysUpdateSystemAttribute)); - if (attrs.Length > 0) - m_AlwaysUpdateSystem = true; -#endif - } - } - - internal void Dispose() - { - DisposeQueries(ref EntityQueries); - DisposeQueries(ref RequiredEntityQueries); - - EntityQueries.Dispose(); - EntityQueries = default; - - RequiredEntityQueries.Dispose(); - RequiredEntityQueries = default; - - if (m_World.IsAllocated) - { - m_World.Free(); - } - - m_JobDependencyForReadingSystems.Dispose(); - m_JobDependencyForWritingSystems.Dispose(); - } - - private void DisposeQueries(ref UnsafeList queries) - { - for (var i = 0; i < queries.Length; ++i) - { - var query = queries[i]; - - if (m_EntityManager.IsQueryValid(query)) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS - query._GetImpl()->_DisallowDisposing = false; -#endif - query.Dispose(); - } - } - } - - public JobHandle Dependency - { - get - { - if (m_GetDependencyFromSafetyManager) - { - var depMgr = m_DependencyManager; - m_GetDependencyFromSafetyManager = false; - m_JobHandle = depMgr->GetDependency(m_JobDependencyForReadingSystems.Ptr, - m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, - m_JobDependencyForWritingSystems.Length); - } - - return m_JobHandle; - } - set - { - m_GetDependencyFromSafetyManager = false; - m_JobHandle = value; - } - } - - public void CompleteDependency() - { - // Previous frame job - m_JobHandle.Complete(); - - // We need to get more job handles from other systems - if (m_GetDependencyFromSafetyManager) - { - m_GetDependencyFromSafetyManager = false; - CompleteDependencyInternal(); - } - } - - internal void CompleteDependencyInternal() - { - m_DependencyManager->CompleteDependenciesNoChecks(m_JobDependencyForReadingSystems.Ptr, - m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, - m_JobDependencyForWritingSystems.Length); - } - - internal void BeforeUpdateVersioning() - { - m_EntityComponentStore->IncrementGlobalSystemVersion(); - ref var qs = ref EntityQueries; - for (int i = 0; i < qs.Length; ++i) - { - qs[i].SetChangedFilterRequiredVersion(m_LastSystemVersion); - } - } - - internal void BeforeOnUpdate() - { - BeforeUpdateVersioning(); - - // We need to wait on all previous frame dependencies, otherwise it is possible that we create infinitely long dependency chains - // without anyone ever waiting on it - m_JobHandle.Complete(); - m_GetDependencyFromSafetyManager = true; - } - -#pragma warning disable 649 - private unsafe struct JobHandleData - { - public void* jobGroup; - public int version; - } -#pragma warning restore 649 - - internal void AfterOnUpdate() - { - AfterUpdateVersioning(); - - var depMgr = m_DependencyManager; - - // If outputJob says no relevant jobs were scheduled, - // then no need to batch them up or register them. - // This is a big optimization if we only Run methods on main thread... - var outputJob = m_JobHandle; - if (((JobHandleData*)&outputJob)->jobGroup != null) - { - JobHandle.ScheduleBatchedJobs(); - m_JobHandle = depMgr->AddDependency(m_JobDependencyForReadingSystems.Ptr, - m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, - m_JobDependencyForWritingSystems.Length, outputJob); - } - } - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] - internal void CheckSafety(ref SystemDependencySafetyUtility.SafetyErrorDetails details, ref bool errorFound) - { - var depMgr = m_DependencyManager; - if (JobsUtility.JobDebuggerEnabled) - { - var dependencyError = SystemDependencySafetyUtility.CheckSafetyAfterUpdate(ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems, depMgr, out details); - - if (dependencyError) - { - SystemDependencySafetyUtility.EmergencySyncAllJobs(ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems, depMgr); - } - - errorFound = dependencyError; - } - } - -#endif - - internal void AfterUpdateVersioning() - { - m_LastSystemVersion = m_EntityComponentStore->GlobalSystemVersion; - } - - internal bool ShouldRunSystem() - { - if (m_AlwaysUpdateSystem) - return true; - - ref var required = ref RequiredEntityQueries; - - if (required.Length > 0) - { - for (int i = 0; i != required.Length; i++) - { - EntityQuery query = required[i]; - if (query.IsEmptyIgnoreFilter) - return false; - } - - return true; - } - else - { - // Systems without queriesDesc should always run. Specifically, - // IJobForEach adds its queriesDesc the first time it's run. - ref var eqs = ref EntityQueries; - var length = eqs.Length; - if (length == 0) - return true; - - // If all the queriesDesc are empty, skip it. - // (There’s no way to know what the key value is without other markup) - for (int i = 0; i != length; i++) - { - EntityQuery query = eqs[i]; - if (!query.IsEmptyIgnoreFilter) - return true; - } - - return false; - } - } - } /// /// A system provides behavior in an ECS architecture. @@ -416,16 +115,13 @@ internal static EntityQuery[] UnsafeListToRefArray(ref UnsafeList o internal void CreateInstance(World world) { m_StatePtr = SystemState.Allocate(); - m_StatePtr->Init(world, GetType()); + m_StatePtr->InitManaged(world, GetType()); OnBeforeCreateInternal(world); try { OnCreateForCompiler(); OnCreate(); - #if ENABLE_PROFILER - m_StatePtr->m_ProfilerMarker = new Profiling.ProfilerMarker($"{world.Name} {TypeManager.GetSystemName(GetType())}"); - #endif } catch { @@ -516,7 +212,6 @@ internal void OnDestroy_Internal() // =================== -#if ENABLE_UNITY_COLLECTIONS_CHECKS internal static ComponentSystemBase ms_ExecutingSystem; public static Type ExecutingSystemType => ms_ExecutingSystem?.GetType(); @@ -536,8 +231,6 @@ internal ComponentSystemBase GetSystemFromSystemID(World world, int systemID) return null; } -#endif - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckExists() { @@ -631,8 +324,7 @@ internal void CompleteDependencyInternal() /// such as an job, to access that type of component inside the job. public ComponentTypeHandle GetComponentTypeHandle(bool isReadOnly = false) where T : struct, IComponentData { - AddReaderWriter(isReadOnly ? ComponentType.ReadOnly() : ComponentType.ReadWrite()); - return EntityManager.GetComponentTypeHandle(isReadOnly); + return CheckedState()->GetComponentTypeHandle(isReadOnly); } /// @@ -645,8 +337,7 @@ public ComponentTypeHandle GetComponentTypeHandle(bool isReadOnly = false) /// job, to access that type of component inside the job. public DynamicComponentTypeHandle GetDynamicComponentTypeHandle(ComponentType componentType) { - AddReaderWriter(componentType); - return EntityManager.GetDynamicComponentTypeHandle(componentType); + return CheckedState()->GetDynamicComponentTypeHandle(componentType); } /// @@ -662,8 +353,7 @@ public DynamicComponentTypeHandle GetDynamicComponentTypeHandle(ComponentType co public BufferTypeHandle GetBufferTypeHandle(bool isReadOnly = false) where T : struct, IBufferElementData { - AddReaderWriter(isReadOnly ? ComponentType.ReadOnly() : ComponentType.ReadWrite()); - return EntityManager.GetBufferTypeHandle(isReadOnly); + return CheckedState()->GetBufferTypeHandle(isReadOnly); } /// @@ -824,51 +514,12 @@ internal void AddReaderWriters(EntityQuery query) // Fast path for singletons internal EntityQuery GetSingletonEntityQueryInternal(ComponentType type) { - var state = CheckedState(); - ref var handles = ref state->EntityQueries; - - for (var i = 0; i != handles.Length; i++) - { - var query = handles[i]; - var queryData = query._GetImpl()->_QueryData; - - // EntityQueries are constructed including the Entity ID - if (2 != queryData->RequiredComponentsCount) - continue; - - if (queryData->RequiredComponents[1] != type) - continue; - - return query; - } - - var newQuery = EntityManager.CreateEntityQuery(&type, 1); - - AddReaderWriters(newQuery); - AfterQueryCreated(newQuery); - - return newQuery; + return CheckedState()->GetSingletonEntityQueryInternal(type); } internal EntityQuery GetEntityQueryInternal(ComponentType* componentTypes, int count) { - var state = CheckedState(); - ref var handles = ref state->EntityQueries; - - for (var i = 0; i != handles.Length; i++) - { - var query = handles[i]; - - if (query.CompareComponents(componentTypes, count)) - return query; - } - - var newQuery = EntityManager.CreateEntityQuery(componentTypes, count); - - AddReaderWriters(newQuery); - AfterQueryCreated(newQuery); - - return newQuery; + return CheckedState()->GetEntityQueryInternal(componentTypes, count); } internal EntityQuery GetEntityQueryInternal(ComponentType[] componentTypes) @@ -881,35 +532,7 @@ internal EntityQuery GetEntityQueryInternal(ComponentType[] componentTypes) internal EntityQuery GetEntityQueryInternal(EntityQueryDesc[] desc) { - var state = CheckedState(); - ref var handles = ref state->EntityQueries; - - for (var i = 0; i != handles.Length; i++) - { - var query = handles[i]; - - if (query.CompareQuery(desc)) - return query; - } - - var newQuery = EntityManager.CreateEntityQuery(desc); - - AddReaderWriters(newQuery); - AfterQueryCreated(newQuery); - - return newQuery; - } - - void AfterQueryCreated(EntityQuery query) - { - var state = CheckedState(); - - query.SetChangedFilterRequiredVersion(state->m_LastSystemVersion); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - query._GetImpl()->_DisallowDisposing = true; -#endif - - state->EntityQueries.Add(query); + return CheckedState()->GetEntityQueryInternal(desc); } /// diff --git a/Unity.Entities/ComponentSystemGroup.cs b/Unity.Entities/ComponentSystemGroup.cs index ba0ff5fc..6bf7045d 100644 --- a/Unity.Entities/ComponentSystemGroup.cs +++ b/Unity.Entities/ComponentSystemGroup.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections; +using Unity.Burst; #if !NET_DOTS using System.Linq; #endif @@ -41,6 +42,11 @@ public unsafe abstract class ComponentSystemGroup : ComponentSystem internal UnsafeList m_UnmanagedSystemsToUpdate; internal UnsafeList m_UnmanagedSystemsToRemove; +#if !UNITY_DOTSRUNTIME + internal delegate bool UnmanagedUpdateSignature(SystemState* state, out SystemDependencySafetyUtility.SafetyErrorDetails errorDetails); + static UnmanagedUpdateSignature s_UnmanagedUpdateFn = BurstCompiler.CompileFunctionPointer(SystemBase.UnmanagedUpdate).Invoke; +#endif + public virtual IEnumerable Systems => m_systemsToUpdate; protected override void OnCreate() @@ -484,12 +490,17 @@ void UpdateAllSystems() SystemState* sys = World.ResolveSystemState(m_UnmanagedSystemsToUpdate[index.Index]); if (sys != null) { - if (SystemBase.UnmanagedUpdate(sys, out var details)) + bool updateError = false; +#if !UNITY_DOTSRUNTIME + updateError = s_UnmanagedUpdateFn(sys, out var details); +#else + updateError = SystemBase.UnmanagedUpdate(sys, out var details); +#endif + + if (updateError) { #if ENABLE_UNITY_COLLECTIONS_CHECKS - var metaIndex = sys->m_UnmanagedMetaIndex; - var systemDebugName = SystemBaseRegistry.GetDebugName(metaIndex); - var errorString = details.FormatToString(systemDebugName); + var errorString = details.FormatToString(sys->DebugName); Debug.LogError(errorString); #endif } diff --git a/Unity.Entities/DebugView.cs b/Unity.Entities/DebugView.cs index 3736e373..54789211 100644 --- a/Unity.Entities/DebugView.cs +++ b/Unity.Entities/DebugView.cs @@ -154,15 +154,17 @@ public int Compare(Entity x, Entity y) } } + unsafe public List Entities { get { - var entities = m_target.GetAllEntities(); + var entities = m_target.GetAllEntitiesImmediate(); entities.Sort(new Comparer()); using (entities) { var result = new List(); + for (var i = 0; i < entities.Length; ++i) result.Add(DebugViewUtility.GetComponents(m_target, entities[i])); return result; diff --git a/Unity.Entities/DefaultWorldInitialization.cs b/Unity.Entities/DefaultWorldInitialization.cs index 024331da..3443aad4 100644 --- a/Unity.Entities/DefaultWorldInitialization.cs +++ b/Unity.Entities/DefaultWorldInitialization.cs @@ -84,7 +84,6 @@ internal static void DomainUnloadOrPlayModeChangeShutdown() if (!s_UnloadOrPlayModeChangeShutdownRegistered) return; - var playerLoop = PlayerLoop.GetCurrentPlayerLoop(); foreach (var w in World.s_AllWorlds) { @@ -132,9 +131,7 @@ public static World Initialize(string defaultWorldName, bool editorWorld = false AddSystemToRootLevelSystemGroupsInternal(world, systemList, systemList.Count); #if !UNITY_DOTSRUNTIME - var playerLoop = PlayerLoop.GetDefaultPlayerLoop(); // TODO(DOTS-2283): shouldn't stomp the default player loop here - ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref playerLoop); - PlayerLoop.SetPlayerLoop(playerLoop); + ScriptBehaviourUpdateOrder.AddWorldToCurrentPlayerLoop(world); #endif DefaultWorldInitialized?.Invoke(world); @@ -171,13 +168,26 @@ public static void AddSystemsToRootLevelSystemGroups(World world, IReadOnlyList< AddSystemToRootLevelSystemGroupsInternal(world, systemTypes, systemTypes.Count); } - private static void AddSystemToRootLevelSystemGroupsInternal(World world, IEnumerable systemTypes, int systemTypesCount) + private static void AddSystemToRootLevelSystemGroupsInternal(World world, IEnumerable systemTypesOrig, int managedTypesCountOrig) { var initializationSystemGroup = world.GetOrCreateSystem(); var simulationSystemGroup = world.GetOrCreateSystem(); var presentationSystemGroup = world.GetOrCreateSystem(); - var systems = world.GetOrCreateSystemsAndLogException(systemTypes, systemTypesCount); + var managedTypes = new List(); + var unmanagedTypes = new List(); + + foreach (var stype in systemTypesOrig) + { + if (typeof(ComponentSystemBase).IsAssignableFrom(stype)) + managedTypes.Add(stype); + else if (typeof(ISystemBase).IsAssignableFrom(stype)) + unmanagedTypes.Add(stype); + else + throw new InvalidOperationException("Bad type"); + } + + var systems = world.GetOrCreateSystemsAndLogException(managedTypes, managedTypes.Count); // Add systems to their groups, based on the [UpdateInGroup] attribute. foreach (var system in systems) @@ -202,43 +212,40 @@ private static void AddSystemToRootLevelSystemGroupsInternal(World world, IEnume foreach (var attr in updateInGroupAttributes) { - var uga = attr as UpdateInGroupAttribute; - if (uga == null) - continue; - - if (!TypeManager.IsSystemAGroup(uga.GroupType)) - { - throw new InvalidOperationException($"Invalid [UpdateInGroup] attribute for {type}: {uga.GroupType} must be derived from ComponentSystemGroup."); - } - if (uga.OrderFirst && uga.OrderLast) + var group = FindGroup(world, type, attr); + if (group != null) { - throw new InvalidOperationException($"The system {type} can not specify both OrderFirst=true and OrderLast=true in its [UpdateInGroup] attribute."); + group.AddSystemToUpdateList(system); } + } + } - var groupSys = world.GetExistingSystem(uga.GroupType); - if (groupSys == null) - { - // Warn against unexpected behaviour combining DisableAutoCreation and UpdateInGroup - var parentDisableAutoCreation = TypeManager.GetSystemAttributes(uga.GroupType, typeof(DisableAutoCreationAttribute)).Length > 0; - if (parentDisableAutoCreation) - { - Debug.LogWarning($"A system {type} wants to execute in {uga.GroupType} but this group has [DisableAutoCreation] and {type} does not. The system will not be added to any group and thus not update."); - } - else - { - Debug.LogWarning( - $"A system {type} could not be added to group {uga.GroupType}, because the group was not created. Fix these errors before continuing. The system will not be added to any group and thus not update."); - } - continue; - } +#if !UNITY_DOTSRUNTIME + // Add unmanaged systems + foreach (var type in unmanagedTypes) + { + SystemHandleUntyped sysHandle = world.CreateUnmanagedSystem(type); - var group = groupSys as ComponentSystemGroup; - if (group != null) + // Add systems to their groups, based on the [UpdateInGroup] attribute. + + var updateInGroupAttributes = TypeManager.GetSystemAttributes(type, typeof(UpdateInGroupAttribute)); + if (updateInGroupAttributes.Length == 0) + { + simulationSystemGroup.AddUnmanagedSystemToUpdateList(sysHandle); + } + + foreach (var attr in updateInGroupAttributes) + { + ComponentSystemGroup groupSys = FindGroup(world, type, attr); + + if (groupSys != null) { - group.AddSystemToUpdateList(system); + groupSys.AddUnmanagedSystemToUpdateList(sysHandle); } } } +#endif + // Update player loop initializationSystemGroup.SortSystems(); @@ -246,6 +253,41 @@ private static void AddSystemToRootLevelSystemGroupsInternal(World world, IEnume presentationSystemGroup.SortSystems(); } + private static ComponentSystemGroup FindGroup(World world, Type systemType, Attribute attr) + { + var uga = attr as UpdateInGroupAttribute; + + if (uga == null) + return null; + + if (!TypeManager.IsSystemAGroup(uga.GroupType)) + { + throw new InvalidOperationException($"Invalid [UpdateInGroup] attribute for {systemType}: {uga.GroupType} must be derived from ComponentSystemGroup."); + } + if (uga.OrderFirst && uga.OrderLast) + { + throw new InvalidOperationException($"The system {systemType} can not specify both OrderFirst=true and OrderLast=true in its [UpdateInGroup] attribute."); + } + + var groupSys = world.GetExistingSystem(uga.GroupType); + if (groupSys == null) + { + // Warn against unexpected behaviour combining DisableAutoCreation and UpdateInGroup + var parentDisableAutoCreation = TypeManager.GetSystemAttributes(uga.GroupType, typeof(DisableAutoCreationAttribute)).Length > 0; + if (parentDisableAutoCreation) + { + Debug.LogWarning($"A system {systemType} wants to execute in {uga.GroupType} but this group has [DisableAutoCreation] and {systemType} does not. The system will not be added to any group and thus not update."); + } + else + { + Debug.LogWarning( + $"A system {systemType} could not be added to group {uga.GroupType}, because the group was not created. Fix these errors before continuing. The system will not be added to any group and thus not update."); + } + } + + return groupSys as ComponentSystemGroup; + } + /// /// Can be called when in edit mode in the editor to initialize a the default world. /// diff --git a/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs b/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs index a75dfb93..906ec1d9 100644 --- a/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs +++ b/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs @@ -46,6 +46,8 @@ internal static void CopyAndReplaceChunks( CloneAndAddChunks(srcEntityManager, dstEntityManager, archetypeChunkChanges.CreatedSrcChunks.Chunks); dstAccess->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, dstAccess->EntityQueryManager); + srcAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); + dstAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); //@TODO-opt: use a query that searches for all chunks that have chunk components on it //@TODO-opt: Move this into a job diff --git a/Unity.Entities/ECBInterop.interop.gen.cs b/Unity.Entities/ECBInterop.interop.gen.cs index 9a88e936..0d18955b 100644 --- a/Unity.Entities/ECBInterop.interop.gen.cs +++ b/Unity.Entities/ECBInterop.interop.gen.cs @@ -14,6 +14,7 @@ using System; using Unity.Burst; +using Unity.Collections; using System.Runtime.InteropServices; namespace Unity.Entities @@ -45,6 +46,7 @@ private struct TagType_RemoveManagedReferences {}; #endif + [NotBurstCompatible] internal static void Initialize() { #if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) diff --git a/Unity.Entities/EntityCommandBuffer.cs b/Unity.Entities/EntityCommandBuffer.cs index f68174a7..8b76de05 100644 --- a/Unity.Entities/EntityCommandBuffer.cs +++ b/Unity.Entities/EntityCommandBuffer.cs @@ -848,6 +848,8 @@ public unsafe partial struct EntityCommandBuffer : IDisposable [NativeDisableUnsafePtrRestriction] internal EntityCommandBufferData* m_Data; + internal int SystemID; + #if ENABLE_UNITY_COLLECTIONS_CHECKS private AtomicSafetyHandle m_Safety0; private AtomicSafetyHandle m_BufferSafety; @@ -865,7 +867,6 @@ internal void WaitForWriterJobs() AtomicSafetyHandle.EnforceAllBufferJobsHaveCompleted(m_ArrayInvalidationSafety); } - internal int SystemID; #if UNITY_2020_1_OR_NEWER private static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate(); @@ -978,6 +979,8 @@ internal EntityCommandBuffer(Allocator label, int disposeSentinelStackDepth, Pla m_Data->m_ThreadedChains = null; m_Data->m_RecordedChainCount = 0; + SystemID = 0; + #if ENABLE_UNITY_COLLECTIONS_CHECKS if (disposeSentinelStackDepth >= 0) { @@ -996,7 +999,6 @@ internal EntityCommandBuffer(Allocator label, int disposeSentinelStackDepth, Pla m_SafetyReadOnlyCount = 0; m_SafetyReadWriteCount = 3; - SystemID = 0; #if UNITY_2020_1_OR_NEWER if (s_staticSafetyId.Data == 0) @@ -1089,7 +1091,24 @@ private void FreeChain(EntityCommandBufferChain* chain, PlaybackPolicy playbackP internal int MainThreadSortKey => Int32.MaxValue; private const bool kBatchableCommand = true; - public Entity CreateEntity(EntityArchetype archetype = new EntityArchetype()) + /// + /// Create an entity with specified archetype. + /// The archetype of the new entity. + public Entity CreateEntity(EntityArchetype archetype) + { + archetype.CheckValidEntityArchetype(); + return _CreateEntity(archetype); + } + + /// + /// Create an entity with no components. + public Entity CreateEntity() + { + EntityArchetype archetype = new EntityArchetype(); + return _CreateEntity(archetype); + } + + private Entity _CreateEntity(EntityArchetype archetype) { EnforceSingleThreadOwnership(); AssertDidNotPlayback(); @@ -1179,15 +1198,14 @@ public void AddComponent(Entity e, ComponentType componentType) /// Records a command to add one or more components to an entity. - /// - /// + /// /// The entity to get additional components. - /// The types of components to add. - public void AddComponent(Entity e, ComponentTypes types) + /// The types of components to add. + public void AddComponent(Entity e, ComponentTypes componentTypes) { EnforceSingleThreadOwnership(); AssertDidNotPlayback(); - m_Data->AddEntityComponentTypesCommand(&m_Data->m_MainThreadChain, MainThreadSortKey, ECBCommand.AddMultipleComponents, e, types); + m_Data->AddEntityComponentTypesCommand(&m_Data->m_MainThreadChain, MainThreadSortKey, ECBCommand.AddMultipleComponents, e, componentTypes); } public void SetComponent(Entity e, T component) where T : struct, IComponentData @@ -1214,7 +1232,7 @@ public void RemoveComponent(Entity e, ComponentType componentType) /// /// /// The entity to have components removed. - /// The types of components to remove. + /// The types of components to remove. public void RemoveComponent(Entity e, ComponentTypes componentTypes) { EnforceSingleThreadOwnership(); @@ -1223,19 +1241,22 @@ public void RemoveComponent(Entity e, ComponentTypes componentTypes) ECBCommand.RemoveMultipleComponents, e, componentTypes); } - /// Records a command to remove one or more components from all entities matching a query. - /// - /// + /// Records a command to remove one or more components from all entities matching a query. + /// The query is performed at playback time, not when the method is called. /// The query specifying which entities to remove the components from. - /// The types of components to remove. - public void RemoveComponent(EntityQuery q, ComponentTypes componentTypes) + /// The types of components to remove. + public void RemoveComponent(EntityQuery entityQuery, ComponentTypes componentTypes) { EnforceSingleThreadOwnership(); AssertDidNotPlayback(); m_Data->AddEntityQueryMultipleComponentsCommand(&m_Data->m_MainThreadChain, MainThreadSortKey, - ECBCommand.RemoveMultipleComponentsEntityQuery, q, componentTypes); + ECBCommand.RemoveMultipleComponentsEntityQuery, entityQuery, componentTypes); } + /// Records a command to add a component to all entities matching a query. + /// The query is performed at playback time, not when the method is called. + /// The query specifying which entities to add the component to. + /// The type of component to add. public void AddComponent(EntityQuery entityQuery, ComponentType componentType) { AssertDidNotPlayback(); @@ -1244,10 +1265,9 @@ public void AddComponent(EntityQuery entityQuery, ComponentType componentType) } /// Records a command to add one or more components to all entities matching a query. - /// - /// + /// The query is performed at playback time, not when the method is called. /// The query specifying which entities get the added components. - /// The types of components to add. + /// The types of components to add. public void AddComponent(EntityQuery entityQuery, ComponentTypes types) { AssertDidNotPlayback(); @@ -1255,11 +1275,19 @@ public void AddComponent(EntityQuery entityQuery, ComponentTypes types) ECBCommand.AddMultipleComponentsEntityQuery, entityQuery, types); } + /// Records a command to add a component to all entities matching a query. + /// The query is performed at playback time, not when the method is called. + /// The query specifying which entities get the added component. + /// The type of component to add. public void AddComponent(EntityQuery entityQuery) { AddComponent(entityQuery, ComponentType.ReadWrite()); } + /// Records a command to remove a component from all entities matching a query. + /// The query is performed at playback time, not when the method is called. + /// The query specifying which entities from which the component is removed. + /// The type of component to remove. public void RemoveComponent(EntityQuery entityQuery, ComponentType componentType) { AssertDidNotPlayback(); @@ -1267,11 +1295,18 @@ public void RemoveComponent(EntityQuery entityQuery, ComponentType componentType ECBCommand.RemoveComponentEntityQuery, entityQuery, componentType); } + /// Records a command to remove a component from all entities matching a query. + /// The query is performed at playback time, not when the method is called. + /// The query specifying which entities from which the component is removed. + /// The type of component to remove. public void RemoveComponent(EntityQuery entityQuery) { RemoveComponent(entityQuery, ComponentType.ReadWrite()); } + /// Records a command to destroy all entities matching a query. + /// The query is performed at playback time, not when the method is called. + /// The query specifying which entities from which the component is removed. public void DestroyEntity(EntityQuery entityQuery) { AssertDidNotPlayback(); @@ -1298,6 +1333,11 @@ public void AddSharedComponent(Entity e, T component) where T : struct, IShar m_Data->AddEntitySharedComponentCommand(&m_Data->m_MainThreadChain, MainThreadSortKey, ECBCommand.AddSharedComponentData, e, hashCode, component); } + /// Records a command to add a shared component to all entities matching a query. + /// The query is performed at playback time, not when the method is called. For entities matching the query which already have + /// this component type, the value is updated. + /// The query specifying which entities to add the component value to. + /// The component value to add. public void AddSharedComponent(EntityQuery entityQuery, T component) where T : struct, ISharedComponentData { EnforceSingleThreadOwnership(); @@ -2096,6 +2136,7 @@ static void ProcessTrackedChanges(EntityDataAccess* mgr, UnsafeList* managedRefe ECBInterop.RemoveManagedReferences(mgr, (int*)managedReferenceIndexRemovalCount->Ptr, count ); mgr->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, mgr->EntityQueryManager); + mgr->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); managedReferenceIndexRemovalCount->Clear(); } @@ -2234,7 +2275,36 @@ private EntityCommandBufferChain* ThreadChain } } - public Entity CreateEntity(int sortKey, EntityArchetype archetype = new EntityArchetype()) + /// + /// Create an entity with specified archetype. + /// Returns the new Entity. + /// A unique index for each set of commands added to the concurrent command buffer + /// across all parallel jobs writing commands to this buffer. The `entityInQueryIndex` argument provided by + /// is an appropriate value to use for this parameter. You can calculate a + /// similar index in an by adding the current entity index within a chunk to the + /// method's `firstEntityIndex` argument. + /// The archetype of the new entity. + public Entity CreateEntity(int sortKey, EntityArchetype archetype) + { + archetype.CheckValidEntityArchetype(); + return _CreateEntity(sortKey, archetype); + } + + /// + /// Create an entity with no components. + /// Returns the new Entity. + /// A unique index for each set of commands added to the concurrent command buffer + /// across all parallel jobs writing commands to this buffer. The `entityInQueryIndex` argument provided by + /// is an appropriate value to use for this parameter. You can calculate a + /// similar index in an by adding the current entity index within a chunk to the + /// method's `firstEntityIndex` argument. + public Entity CreateEntity(int sortKey) + { + EntityArchetype archetype = new EntityArchetype(); + return _CreateEntity(sortKey, archetype); + } + + private Entity _CreateEntity(int sortKey, EntityArchetype archetype) { CheckWriteAccess(); var chain = ThreadChain; diff --git a/Unity.Entities/EntityCommandBuffer.interop.gen.cs b/Unity.Entities/EntityCommandBuffer.interop.gen.cs index 6b8830a8..2a2e68ab 100644 --- a/Unity.Entities/EntityCommandBuffer.interop.gen.cs +++ b/Unity.Entities/EntityCommandBuffer.interop.gen.cs @@ -14,6 +14,7 @@ using System; using Unity.Burst; +using Unity.Collections; using System.Runtime.InteropServices; namespace Unity.Entities @@ -43,6 +44,7 @@ private static bool UseDelegate() #endif + [NotBurstCompatible] internal static void Initialize() { #if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) diff --git a/Unity.Entities/EntityCommandBufferSystem.cs b/Unity.Entities/EntityCommandBufferSystem.cs index 5f1d6856..da50ace1 100644 --- a/Unity.Entities/EntityCommandBufferSystem.cs +++ b/Unity.Entities/EntityCommandBufferSystem.cs @@ -80,9 +80,7 @@ internal NativeList PendingBuffers public EntityCommandBuffer CreateCommandBuffer() { var cmds = new EntityCommandBuffer(Allocator.TempJob, -1, PlaybackPolicy.SinglePlayback); -#if ENABLE_UNITY_COLLECTIONS_CHECKS cmds.SystemID = ms_ExecutingSystem != null ? ms_ExecutingSystem.CheckedState()->m_SystemID : 0; -#endif m_PendingBuffers.Add(cmds); return cmds; diff --git a/Unity.Entities/EntityComponentStore.cs b/Unity.Entities/EntityComponentStore.cs index 1470a87f..31067ae2 100644 --- a/Unity.Entities/EntityComponentStore.cs +++ b/Unity.Entities/EntityComponentStore.cs @@ -1,6 +1,3 @@ -#if !UNITY_WEBGL && (UNITY_2020_1_OR_NEWER || UNITY_DOTSRUNTIME) -#define USE_VIRTUAL_MEMORY -#endif using System; using System.Diagnostics; using System.Threading; @@ -291,6 +288,8 @@ internal unsafe partial struct EntityComponentStore internal ManagedDeferredCommands ManagedChangesTracker; + internal ChunkListChanges m_ChunkListChangesTracker; + ulong m_NextChunkSequenceNumber; int m_NextFreeEntityIndex; @@ -316,13 +315,17 @@ internal unsafe partial struct EntityComponentStore const int kMaximumEmptyChunksInPool = 16; // can't alloc forever const int kDefaultCapacity = 1024; - const int kMaxSharedComponentCount = 8; + internal const int kMaxSharedComponentCount = 8; + + struct AddressSpaceTagType { } + static readonly SharedStatic s_TotalChunkAddressSpaceInBytes = SharedStatic.GetOrCreate(); + + static readonly ulong DefaultChunkAddressSpaceInBytes = 1024UL * 1024UL * 1024UL; - static ulong s_TotalChunkAddressSpaceInBytes = 1024UL * 1024UL * 1024UL; public static ulong TotalChunkAddressSpaceInBytes { - get => s_TotalChunkAddressSpaceInBytes; - set => s_TotalChunkAddressSpaceInBytes = value; + get => s_TotalChunkAddressSpaceInBytes.Data > 0 ? s_TotalChunkAddressSpaceInBytes.Data - 1 : DefaultChunkAddressSpaceInBytes; + set => s_TotalChunkAddressSpaceInBytes.Data = value + 1; } #if UNITY_EDITOR @@ -447,6 +450,9 @@ public static void Create(EntityComponentStore* entities, ulong startChunkSequen entities->m_EntityComponentType = ComponentType.ReadWrite(); entities->InitializeTypeManagerPointers(); + entities->m_ChunkListChangesTracker = new ChunkListChanges(); + entities->m_ChunkListChangesTracker.Init(); + #if USE_VIRTUAL_MEMORY s_chunkStore.Data.Initialize(TotalChunkAddressSpaceInBytes); #endif @@ -534,6 +540,7 @@ void Dispose() archetype->Chunks.Dispose(); archetype->ChunksWithEmptySlots.Dispose(); archetype->FreeChunksBySharedComponents.Dispose(); + archetype->MatchingQueryData.Dispose(); } m_Archetypes.Dispose(); @@ -921,7 +928,7 @@ public enum ComponentOperation } [BurstCompile] - internal struct EntityBatchFromEntityChunkDataShared : IJobBurstScheduable + internal struct EntityBatchFromEntityChunkDataShared : IJobBurstSchedulable { [ReadOnly] public NativeArray EntityChunkData; public NativeList EntityBatchList; @@ -1018,7 +1025,7 @@ public bool CreateEntityBatchListForRemoveComponent(NativeArray entities } [BurstCompile] - internal struct SortEntityInChunk : IJobBurstScheduable + internal struct SortEntityInChunk : IJobBurstSchedulable { public NativeArray EntityInChunks; public void Execute() @@ -1080,7 +1087,7 @@ public bool CreateEntityBatchList(NativeArray entities, ComponentOperati } [BurstCompile] - internal struct GatherEntityInChunkForEntities : IJobParallelForBurstScheduable + internal struct GatherEntityInChunkForEntities : IJobParallelForBurstSchedulable { [ReadOnly][NativeDisableUnsafePtrRestriction] public Entity* Entities; @@ -1202,8 +1209,8 @@ struct ChunkStore long m_pagesCommitted; VMRange m_chunkAddressSpace; - public long ReservedBytes => m_chunkAddressSpace.SizeInBytes; - public long CommittedBytes => m_pagesCommitted * m_chunkAddressSpace.PageSizeInBytes; + public ulong ReservedBytes => m_chunkAddressSpace.SizeInBytes; + public ulong CommittedBytes => (ulong)(m_pagesCommitted * m_chunkAddressSpace.PageSizeInBytes); public long ReservedPages => m_chunkAddressSpace.pageCount; public long CommittedPages => m_pagesCommitted; @@ -1238,12 +1245,20 @@ public int Initialize(ulong sizeOfAddressRangeInBytes) } #if USE_VIRTUAL_MEMORY - BaselibErrorState errorState = default; - m_pagesCommitted = 0; - m_chunkAddressSpace = VirtualMemoryUtility.ReserveAddressSpace(sizeOfAddressRangeInBytes / VirtualMemoryUtility.DefaultPageSizeInBytes, (ulong)VirtualMemoryUtility.DefaultPageSizeInBytes, out errorState); + BaselibErrorState errorState = default; + m_chunkAddressSpace = VirtualMemoryUtility.ReserveAddressSpace(sizeOfAddressRangeInBytes / VirtualMemoryUtility.DefaultPageSizeInBytes, VirtualMemoryUtility.DefaultPageSizeInBytes, out errorState); + VirtualMemoryUtility.ReportWrappedBaselibError(errorState); + if(errorState.OutOfMemory) + { + FixedString512 error = "Failed to reserve "; + error.Append(sizeOfAddressRangeInBytes); + FixedString128 e2 = " bytes. System ran out of memory."; + error.Append(e2); + Debug.LogError(error); + } m_MegachunkSizeInPages = (uint)(((MegachunkSizeInBytes + m_chunkAddressSpace.PageSizeInBytes - 1) & ~(m_chunkAddressSpace.PageSizeInBytes - 1)) / m_chunkAddressSpace.PageSizeInBytes); #endif @@ -1260,6 +1275,11 @@ public void Dispose() BaselibErrorState errorState = default; VirtualMemoryUtility.FreeAddressSpace(m_chunkAddressSpace, out errorState); VirtualMemoryUtility.ReportWrappedBaselibError(errorState); + if(errorState.InvalidAddressRange) + { + FixedString512 error = "Failed to free address range because it is was never reserved."; + Debug.LogError(error); + } #endif } } @@ -1289,11 +1309,28 @@ public int AllocateChunk(out Chunk* value) IntPtr allocationPtr = (IntPtr)((long)m_chunkAddressSpace.ptr + megachunkIndex * MegachunkSizeInBytes); VMRange allocationRange = new VMRange { ptr = allocationPtr, log2PageSize = m_chunkAddressSpace.log2PageSize, pageCount = m_MegachunkSizeInPages }; + + if (m_pagesCommitted + m_MegachunkSizeInPages > m_chunkAddressSpace.pageCount) + { + FixedString512 error = "You have committed all virtual memory reserved for Chunks ("; + error.Append(CommittedBytes); + FixedString128 e2 = " bytes). To allocate more than "; + error.Append(e2); + error.Append((CommittedBytes / (ulong)ChunkSizeInBytesRoundedUpToPow2)); + e2 = " Chunks, you must reserve a larger virtual address range by setting "; + error.Append(e2); + e2 = nameof(TotalChunkAddressSpaceInBytes); + error.Append(e2); + e2 = " during World initialization."; + error.Append(e2); + Debug.LogError(error); + } + VirtualMemoryUtility.CommitMemory(allocationRange, out errorState); VirtualMemoryUtility.ReportWrappedBaselibError(errorState); long allocated = (long)allocationRange.ptr; - if (errorState.code != 0) + if (!errorState.Success) { AllocationFailed(megachunkIndex, bit); return (int)errorState.code; @@ -1368,7 +1405,7 @@ public int FreeChunk(Chunk* value) VirtualMemoryUtility.DecommitMemory(rangeToFree, out errorState); VirtualMemoryUtility.ReportWrappedBaselibError(errorState); - if (errorState.code != 0) + if (!errorState.Success) { return (int)errorState.code; } @@ -1390,7 +1427,7 @@ public int FreeChunk(Chunk* value) private static readonly SharedStatic s_chunkStore = SharedStatic.GetOrCreate(); - public static void GetChunkMemoryStats(out long reservedPages, out long committedPages, out long reservedBytes, out long committedBytes, out long pageSizeInBytes) + public static void GetChunkMemoryStats(out long reservedPages, out long committedPages, out ulong reservedBytes, out ulong committedBytes, out long pageSizeInBytes) { #if USE_VIRTUAL_MEMORY reservedPages = s_chunkStore.Data.ReservedPages; @@ -1540,6 +1577,8 @@ static int CalculateChunkCapacity(int bufferSize, ushort* componentSizes, int co dstArchetype->EntityCount = 0; dstArchetype->Chunks = new ArchetypeChunkData(count, numSharedComponents); dstArchetype->ChunksWithEmptySlots = new UnsafeChunkPtrList(0, Allocator.Persistent); + dstArchetype->MatchingQueryData = new UnsafePtrList(0, Allocator.Persistent); + dstArchetype->NextChangedArchetype = null; dstArchetype->InstantiateArchetype = null; dstArchetype->CopyArchetype = null; dstArchetype->MetaChunkArchetype = null; @@ -1788,6 +1827,46 @@ public void EndArchetypeChangeTracking(ArchetypeChanges changes, EntityQueryMana queries->AddAdditionalArchetypes(changeList); } + + internal struct ChunkListChanges + { + public Archetype* ArchetypeTrackingHead; + + public void Init() + { + ArchetypeTrackingHead = null; + } + + public void TrackArchetype(Archetype* archetype) + { + if (archetype->NextChangedArchetype == null) + { + archetype->NextChangedArchetype = ArchetypeTrackingHead; + ArchetypeTrackingHead = archetype; + } + } + } + + public void InvalidateChunkListCacheForChangedArchetypes() + { + var archetype = m_ChunkListChangesTracker.ArchetypeTrackingHead; + while(archetype != null) + { + var matchingQueryCount = archetype->MatchingQueryData.Length; + for (int queryIndex = 0; queryIndex < matchingQueryCount; ++queryIndex) + { + var queryData = (EntityQueryData*) archetype->MatchingQueryData.Ptr[queryIndex]; + queryData->MatchingChunkCache.InvalidateCache(); + } + + var nextArchetype = archetype->NextChangedArchetype; + archetype->NextChangedArchetype = null; + archetype = nextArchetype; + } + + m_ChunkListChangesTracker.ArchetypeTrackingHead = null; + } + public int ManagedComponentIndexUsedCount => m_ManagedComponentIndex - 1 - m_ManagedComponentFreeIndex.Length / 4; public int ManagedComponentFreeCount => m_ManagedComponentIndexCapacity - m_ManagedComponentIndex + m_ManagedComponentFreeIndex.Length / 4; diff --git a/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs b/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs index 4cd48ec2..7d5b63d2 100644 --- a/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs +++ b/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs @@ -519,7 +519,7 @@ public static JobHandle GetCreatedAndDestroyedEntities(EntityComponentStore* sto } [BurstCompile] - internal struct GetOrCreateDestroyedEntitiesJob : IJobBurstScheduable + internal struct GetOrCreateDestroyedEntitiesJob : IJobBurstSchedulable { public NativeList State; public NativeList CreatedEntities; diff --git a/Unity.Entities/EntityComponentStoreDebug.cs b/Unity.Entities/EntityComponentStoreDebug.cs index 37ee5d07..6bf0db9f 100644 --- a/Unity.Entities/EntityComponentStoreDebug.cs +++ b/Unity.Entities/EntityComponentStoreDebug.cs @@ -201,7 +201,7 @@ void AssertArchetypeComponents(ComponentTypeInArchetype* types, int count) { if (count < 1) throw new ArgumentException($"Invalid component count"); - + // NOTE: LookUpCache / ComponentDataFromEntity uses short for the IndexInArchetype cache if (count >= short.MaxValue) throw new ArgumentException($"Archetypes can have a maximum of {short.MaxValue} components."); @@ -325,6 +325,31 @@ public void AssertCanAddComponent(UnsafeMatchingArchetypePtrList archetypeList, AssertCanAddComponent(archetypeList.Ptr[i]->Archetype, componentType); } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] + public void AssertCanAddComponents(UnsafeMatchingArchetypePtrList archetypeList, ComponentTypes componentTypes) + { + int newShared = 0; + int totalNewComponentSize = 0; + for (int i = 0; i < componentTypes.Length; i++) + { + var componentType = componentTypes.GetComponentType(i); + if (componentType == m_EntityComponentType) + throw new ArgumentException("Cannot add Entity as a component."); + if (componentType.IsSharedComponent) + newShared++; + totalNewComponentSize += GetComponentArraySize(GetTypeInfo(componentType.TypeIndex).SizeInChunk, 1); + } + + for (int i = 0; i < archetypeList.Length; i++) + { + var archetype = archetypeList.Ptr[i]->Archetype; + if ((archetype->NumSharedComponents + newShared) > kMaxSharedComponentCount) + throw new InvalidOperationException($"Cannot add more than {kMaxSharedComponentCount} SharedComponent to a single Archetype"); + if ((archetype->InstanceSizeWithOverhead + totalNewComponentSize) > Chunk.GetChunkBufferSize()) + throw new InvalidOperationException("Entity archetype component data is too large. Previous archetype size per instance {archetype->InstanceSizeWithOverhead} bytes. Attempting to add component size {componentInstanceSize} bytes. Maximum chunk size {chunkDataSize}."); + } + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] public void AssertCanAddComponents(Entity entity, ComponentTypes types) { @@ -447,7 +472,7 @@ public static void AssertArchetypeDoesNotRemoveSystemStateComponents(Archetype* int o = 0; int n = 0; - for (; n < src->TypesCount && o < dst->TypesCount;) + for (; o < src->TypesCount && n < dst->TypesCount;) { int srcType = src->Types[o].TypeIndex; int dstType = dst->Types[n].TypeIndex; @@ -475,18 +500,27 @@ public static void AssertArchetypeDoesNotRemoveSystemStateComponents(Archetype* } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] - public void AssertCanAddChunkComponent(NativeArray chunkArray, ComponentType componentType) + public void CheckCanAddChunkComponent(NativeArray chunkArray, ComponentType componentType, ref bool result) { var chunks = (ArchetypeChunk*)chunkArray.GetUnsafeReadOnlyPtr(); + for (int i = 0; i < chunkArray.Length; ++i) { var chunk = chunks[i].m_Chunk; if (ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, componentType.TypeIndex) != -1) - throw new ArgumentException( - $"A chunk component with type:{componentType} has already been added to the chunk."); + { + result = false; + return; + } } } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] + public void ThrowDuplicateChunkComponentError(ComponentType componentType) + { + throw new ArgumentException($"A chunk component with type:{componentType} has already been added to the chunk."); + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] public void AssertCanInstantiateEntities(Entity srcEntity, Entity* outputEntities, int instanceCount) { diff --git a/Unity.Entities/EntityDataAccess.cs b/Unity.Entities/EntityDataAccess.cs index 2f71338c..60204295 100644 --- a/Unity.Entities/EntityDataAccess.cs +++ b/Unity.Entities/EntityDataAccess.cs @@ -63,7 +63,10 @@ unsafe struct EntityDataAccess : IDisposable private static readonly SharedStatic s_ManagedPlaybackTrampoline = SharedStatic.GetOrCreate(); private static object s_DelegateGCPrevention; + [NotBurstCompatible] internal ManagedEntityDataAccess ManagedEntityDataAccess => ManagedEntityDataAccess.GetInstance(m_ManagedAccessHandle); + + [NotBurstCompatible] internal ManagedComponentStore ManagedComponentStore => ManagedEntityDataAccess.m_ManagedComponentStore; // These pointer attributes freak debuggers out because they take @@ -133,7 +136,7 @@ internal ref UnsafeHashMap AliveEntityQueries internal bool IsInExclusiveTransaction => m_IsInExclusiveTransaction; [BurstCompile] - internal struct DestroyChunks : IJobBurstScheduable + internal struct DestroyChunks : IJobBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* EntityComponentStore; @@ -459,7 +462,9 @@ public Entity CreateEntity(EntityArchetype archetype) { if (!IsInExclusiveTransaction) BeforeStructuralChange(); + Entity entity = CreateEntityDuringStructuralChange(archetype); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); return entity; } @@ -482,7 +487,10 @@ internal void CreateEntity(EntityArchetype archetype, Entity* outEntities, int c { if (!IsInExclusiveTransaction) BeforeStructuralChange(); + StructuralChange.CreateEntity(EntityComponentStore, archetype.Archetype, outEntities, count); + + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); Assert.IsTrue(EntityComponentStore->ManagedChangesTracker.Empty); } @@ -533,6 +541,7 @@ public bool AddComponent(Entity entity, ComponentType componentType) var result = AddComponentDuringStructuralChange(entity, componentType); EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); return result; @@ -583,6 +592,7 @@ public void AddComponent(UnsafeMatchingArchetypePtrList archetypeList, EntityQue EntityComponentStore->AddComponentWithValidation(archetypeList, filter, componentType, DependencyManager); EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -621,6 +631,28 @@ internal void AddComponentsDuringStructuralChange(UnsafeMatchingArchetypePtrList chunks.Dispose(); } + internal void AddComponents(UnsafeMatchingArchetypePtrList archetypeList, EntityQueryFilter filter, ComponentTypes types) + { + AssertMainThread(); + EntityComponentStore->AssertCanAddComponents(archetypeList, types); + + var chunks = ChunkIterationUtility.CreateArchetypeChunkArray(archetypeList, Allocator.TempJob, ref filter, DependencyManager); + + if (chunks.Length > 0) + { + BeforeStructuralChange(); + var archetypeChanges = EntityComponentStore->BeginArchetypeChangeTracking(); + + StructuralChange.AddComponentsChunks(EntityComponentStore, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, ref types); + + EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); + PlaybackManagedChanges(); + } + + chunks.Dispose(); + } + public bool RemoveComponent(Entity entity, ComponentType componentType) { if (!IsInExclusiveTransaction) @@ -631,6 +663,7 @@ public bool RemoveComponent(Entity entity, ComponentType componentType) var removed = RemoveComponentDuringStructuralChange(entity, componentType); EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); return removed; @@ -710,6 +743,7 @@ internal void RemoveComponent(NativeArray chunks, ComponentType StructuralChange.RemoveComponentChunks(EntityComponentStore, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, componentType.TypeIndex); EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -731,6 +765,7 @@ public bool HasComponent(Entity entity, ComponentType type) return EntityComponentStore->HasComponent(entity, type); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public T GetComponentData(Entity entity) where T : struct, IComponentData { var typeIndex = TypeManager.GetTypeIndex(); @@ -758,6 +793,7 @@ public T GetComponentData(Entity entity) where T : struct, IComponentData return EntityComponentStore->GetComponentDataRawRWEntityHasComponent(entity, typeIndex); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void SetComponentData(Entity entity, T componentData) where T : struct, IComponentData { var typeIndex = TypeManager.GetTypeIndex(); @@ -778,6 +814,7 @@ public void SetComponentDataRaw(Entity entity, int typeIndex, void* data, int si EntityComponentStore->SetComponentDataRawEntityHasComponent(entity, typeIndex, data, size); } + [NotBurstCompatible] public bool AddSharedComponentData(Entity entity, T componentData, ManagedComponentStore managedComponentStore) where T : struct, ISharedComponentData { //TODO: optimize this (no need to move the entity to a new chunk twice) @@ -796,6 +833,7 @@ public bool AddSharedComponentData(Entity entity, T componentData, ManagedCom /// /// /// + [NotBurstCompatible] public bool AddSharedComponentDataDuringStructuralChange(Entity entity, T componentData) where T : struct, ISharedComponentData { //TODO: optimize this (no need to move the entity to a new chunk twice) @@ -804,6 +842,7 @@ public bool AddSharedComponentDataDuringStructuralChange(Entity entity, T com return added; } + [NotBurstCompatible] public void AddSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int typeIndex, int hashCode, object componentData, ManagedComponentStore managedComponentStore) { //TODO: optimize this (no need to move the entity to a new chunk twice) @@ -821,6 +860,7 @@ public void AddSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int type /// /// /// + [NotBurstCompatible] public bool AddSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(Entity entity, int typeIndex, int hashCode, object componentData, UnsafeList* managedReferenceIndexRemovalCount) { //TODO: optimize this (no need to move the entity to a new chunk twice) @@ -830,6 +870,7 @@ public bool AddSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(E return added; } + [NotBurstCompatible] public void AddSharedComponentDataBoxedDefaultMustBeNull(UnsafeMatchingArchetypePtrList archetypeList, EntityQueryFilter filter, int typeIndex, int hashCode, object componentData, ManagedComponentStore managedComponentStore) { AssertMainThread(); @@ -861,6 +902,7 @@ public void AddSharedComponentDataBoxedDefaultMustBeNull(UnsafeMatchingArchetype /// /// /// + [NotBurstCompatible] public void AddSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(UnsafeMatchingArchetypePtrList archetypeList, EntityQueryFilter filter, int typeIndex, int hashCode, object componentData, UnsafeList* managedReferenceIndexRemovalCount) { AssertMainThread(); @@ -890,6 +932,7 @@ internal void AddSharedComponentData(NativeArray chunks, int sha StructuralChange.AddSharedComponentChunks(EntityComponentStore, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, componentType.TypeIndex, sharedComponentIndex); EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, EntityQueryManager); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -943,6 +986,7 @@ internal void AddSharedComponentDataDuringStructuralChange(NativeArray(Entity entity, ManagedComponentStore managedComponentStore) where T : struct, ISharedComponentData { var typeIndex = TypeManager.GetTypeIndex(); @@ -952,6 +996,7 @@ public T GetSharedComponentData(Entity entity, ManagedComponentStore managedC return managedComponentStore.GetSharedComponentData(sharedComponentIndex); } + [NotBurstCompatible] public void SetSharedComponentData(Entity entity, T componentData, ManagedComponentStore managedComponentStore) where T : struct, ISharedComponentData { if (!IsInExclusiveTransaction) @@ -963,10 +1008,12 @@ public void SetSharedComponentData(Entity entity, T componentData, ManagedCom var newSharedComponentDataIndex = managedComponentStore.InsertSharedComponent(componentData); EntityComponentStore->SetSharedComponentDataIndex(entity, componentType, newSharedComponentDataIndex); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); managedComponentStore.Playback(ref EntityComponentStore->ManagedChangesTracker); managedComponentStore.RemoveReference(newSharedComponentDataIndex); } + [NotBurstCompatible] public void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int typeIndex, int hashCode, object componentData, ManagedComponentStore managedComponentStore) { if (!IsInExclusiveTransaction) @@ -980,6 +1027,7 @@ public void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int type var componentType = ComponentType.FromTypeIndex(typeIndex); EntityComponentStore->SetSharedComponentDataIndex(entity, componentType, newSharedComponentDataIndex); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); ManagedComponentStore.Playback(ref EntityComponentStore->ManagedChangesTracker); ManagedComponentStore.RemoveReference(newSharedComponentDataIndex); } @@ -995,6 +1043,7 @@ public void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int type /// /// /// + [NotBurstCompatible] public void SetSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(Entity entity, int typeIndex, int hashCode, object componentData, UnsafeList* managedReferenceIndexRemovalCount) { EntityComponentStore->AssertEntityHasComponent(entity, typeIndex); @@ -1009,6 +1058,7 @@ public void SetSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(E managedReferenceIndexRemovalCount->Add(newSharedComponentDataIndex); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public DynamicBuffer GetBuffer(Entity entity #if ENABLE_UNITY_COLLECTIONS_CHECKS , AtomicSafetyHandle safety, AtomicSafetyHandle arrayInvalidationSafety @@ -1065,6 +1115,7 @@ internal void InstantiateInternal(Entity srcEntity, Entity* outputEntities, int EntityComponentStore->AssertEntitiesExist(&srcEntity, 1); EntityComponentStore->AssertCanInstantiateEntities(srcEntity, outputEntities, count); StructuralChange.InstantiateEntities(EntityComponentStore, &srcEntity, outputEntities, count); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -1099,6 +1150,7 @@ internal void InstantiateInternal(Entity* srcEntities, Entity* outputEntities, i EntityComponentStore->AssertEntitiesExist(srcEntities, count); EntityComponentStore->AssertCanInstantiateEntities(srcEntities, count, removePrefab); EntityComponentStore->InstantiateEntities(srcEntities, outputEntities, count, removePrefab); + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -1108,7 +1160,10 @@ internal void DestroyEntityInternal(Entity* entities, int count) BeforeStructuralChange(); EntityComponentStore->AssertValidEntities(entities, count); + EntityComponentStore->DestroyEntities(entities, count); + + EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); PlaybackManagedChanges(); } @@ -1136,6 +1191,7 @@ public void SwapComponents(ArchetypeChunk leftChunk, int leftIndex, ArchetypeChu globalSystemVersion, globalSystemVersion); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public BufferTypeHandle GetBufferTypeHandle(bool isReadOnly) where T : struct, IBufferElementData { diff --git a/Unity.Entities/EntityManager.cs b/Unity.Entities/EntityManager.cs index 21046ccd..f822c1f2 100644 --- a/Unity.Entities/EntityManager.cs +++ b/Unity.Entities/EntityManager.cs @@ -124,6 +124,7 @@ private class StructuralChangeMethodAttribute : Attribute /// The of this EntityManager. /// /// A World has one EntityManager and an EntityManager manages the entities of one World. + [NotBurstCompatible] public World World => GetCheckedEntityDataAccess()->ManagedEntityDataAccess.m_World; /// @@ -165,6 +166,7 @@ private class StructuralChangeMethodAttribute : Attribute /// /// An object providing debugging information and operations. /// + [NotBurstCompatible] public EntityManagerDebug Debug { get @@ -185,6 +187,7 @@ public static ulong TotalChunkAddressSpaceInBytes set => Entities.EntityComponentStore.TotalChunkAddressSpaceInBytes = value; } + [NotBurstCompatible] internal void Initialize(World world) { TypeManager.Initialize(); @@ -194,11 +197,10 @@ internal void Initialize(World world) ChunkIterationUtility.Initialize(); // Pick any recorded types that have come in after a domain reload. + EarlyInitHelpers.FlushEarlyInits(); SystemBaseRegistry.InitializePendingTypes(); -#if !UNITY_DOTSRUNTIME CreateJobReflectionData(); -#endif #if ENABLE_UNITY_COLLECTIONS_CHECKS m_Safety = AtomicSafetyHandle.Create(); @@ -218,32 +220,30 @@ internal void Initialize(World world) EntityDataAccess.Initialize(m_EntityDataAccess, world); } -#if !UNITY_DOTSRUNTIME private void CreateJobReflectionData() { // Until we have reliable IL postprocessing or code generation we will have to resort to making these initialization calls manually. - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - IJobBurstScheduableExtensions.JobStruct.Initialize(); - - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); - IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + IJobBurstSchedulableExtensions.JobStruct.Initialize(); + + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); + IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable.Initialize(); } -#endif internal void PreDisposeCheck() { @@ -280,6 +280,7 @@ public bool Equals(EntityManager other) return m_EntityDataAccess == other.m_EntityDataAccess; } + [NotBurstCompatible] public override bool Equals(object obj) { return obj is EntityManager other && Equals(other); diff --git a/Unity.Entities/EntityManagerAccessComponentData.cs b/Unity.Entities/EntityManagerAccessComponentData.cs index 372e7e96..aab23982 100644 --- a/Unity.Entities/EntityManagerAccessComponentData.cs +++ b/Unity.Entities/EntityManagerAccessComponentData.cs @@ -29,6 +29,7 @@ public int GetSharedComponentCount() /// The type of component to retrieve. /// A struct of type T containing the component value. /// Thrown if the component type has no fields. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public T GetComponentData(Entity entity) where T : struct, IComponentData { var ecs = GetCheckedEntityDataAccess(); @@ -42,6 +43,7 @@ public T GetComponentData(Entity entity) where T : struct, IComponentData /// The data to set. /// The component type. /// Thrown if the component type has no fields. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void SetComponentData(Entity entity, T componentData) where T : struct, IComponentData { var ecs = GetCheckedEntityDataAccess(); @@ -59,6 +61,7 @@ public void SetComponentData(Entity entity, T componentData) where T : struct /// The component type. /// A struct of type T containing the component value. /// Thrown if the ArchetypeChunk object is invalid. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public T GetChunkComponentData(ArchetypeChunk chunk) where T : struct, IComponentData { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -80,6 +83,7 @@ public T GetChunkComponentData(ArchetypeChunk chunk) where T : struct, ICompo /// The entity. /// The component type. /// A struct of type T containing the component value. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public T GetChunkComponentData(Entity entity) where T : struct, IComponentData { var ecs = GetCheckedEntityDataAccess(); @@ -102,6 +106,7 @@ public T GetChunkComponentData(Entity entity) where T : struct, IComponentDat /// The component type. /// Thrown if the ArchetypeChunk object is invalid. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void SetChunkComponentData(ArchetypeChunk chunk, T componentValue) where T : struct, IComponentData { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -120,6 +125,7 @@ public void SetChunkComponentData(ArchetypeChunk chunk, T componentValue) whe /// The entity. /// The type of the managed object. /// The managed object, cast to type T. + [NotBurstCompatible] public T GetComponentObject(Entity entity) { var ecs = GetCheckedEntityDataAccess(); @@ -127,6 +133,7 @@ public T GetComponentObject(Entity entity) return ecs->GetComponentObject(entity, ComponentType.ReadWrite(), mcs); } + [NotBurstCompatible] public T GetComponentObject(Entity entity, ComponentType componentType) { var ecs = GetCheckedEntityDataAccess(); @@ -151,6 +158,7 @@ public T GetComponentObject(Entity entity, ComponentType componentType) /// A shared component object containing the values to set. /// The shared component type. [StructuralChangeMethod] + [NotBurstCompatible] public void SetSharedComponentData(Entity entity, T componentData) where T : struct, ISharedComponentData { var ecs = GetCheckedEntityDataAccess(); @@ -173,6 +181,7 @@ public void SetSharedComponentData(Entity entity, T componentData) where T : /// A shared component object containing the values to set. /// The shared component type. [StructuralChangeMethod] + [NotBurstCompatible] public void SetSharedComponentData(EntityQuery query, T componentData) where T : struct, ISharedComponentData { using (var chunks = query.CreateArchetypeChunkArray(Allocator.TempJob)) @@ -208,6 +217,7 @@ public T GetSharedComponentData(Entity entity) where T : struct, ISharedCompo return ecs->GetSharedComponentData(entity, mcs); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleSharedComponentData) })] public int GetSharedComponentDataIndex(Entity entity) where T : struct, ISharedComponentData { var ecs = GetCheckedEntityDataAccess()->EntityComponentStore; @@ -290,6 +300,7 @@ public void GetAllUniqueSharedComponentData(List sharedComponentValues, Li /// The type of the buffer's elements. /// The DynamicBuffer object for accessing the buffer contents. /// Thrown if T is an unsupported type. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public DynamicBuffer GetBuffer(Entity entity) where T : struct, IBufferElementData { var typeIndex = TypeManager.GetTypeIndex(); @@ -360,6 +371,7 @@ public int GetComponentCount(Entity entity) // INTERNAL // ---------------------------------------------------------------------------------------------------------- + [NotBurstCompatible] internal void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int typeIndex, object componentData) { var hashCode = 0; @@ -369,6 +381,7 @@ internal void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int ty SetSharedComponentDataBoxedDefaultMustBeNull(entity, typeIndex, hashCode, componentData); } + [NotBurstCompatible] void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int typeIndex, int hashCode, object componentData) { var ecs = GetCheckedEntityDataAccess(); @@ -376,6 +389,7 @@ void SetSharedComponentDataBoxedDefaultMustBeNull(Entity entity, int typeIndex, ecs->SetSharedComponentDataBoxedDefaultMustBeNull(entity, typeIndex, hashCode, componentData, mcs); } + [NotBurstCompatible] internal void SetComponentObject(Entity entity, ComponentType componentType, object componentObject) { var ecs = GetCheckedEntityDataAccess(); @@ -383,6 +397,7 @@ internal void SetComponentObject(Entity entity, ComponentType componentType, obj ecs->SetComponentObject(entity, componentType, componentObject, mcs); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] internal ComponentDataFromEntity GetComponentDataFromEntity(bool isReadOnly = false) where T : struct, IComponentData { @@ -390,6 +405,7 @@ internal ComponentDataFromEntity GetComponentDataFromEntity(bool isReadOnl return GetComponentDataFromEntity(typeIndex, isReadOnly); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] internal ComponentDataFromEntity GetComponentDataFromEntity(int typeIndex, bool isReadOnly) where T : struct, IComponentData { @@ -407,12 +423,14 @@ internal ComponentDataFromEntity GetComponentDataFromEntity(int typeIndex, #endif } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] internal BufferFromEntity GetBufferFromEntity(bool isReadOnly = false) where T : struct, IBufferElementData { return GetBufferFromEntity(TypeManager.GetTypeIndex(), isReadOnly); } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] internal BufferFromEntity GetBufferFromEntity(int typeIndex, bool isReadOnly = false) where T : struct, IBufferElementData { diff --git a/Unity.Entities/EntityManagerChangeArchetype.cs b/Unity.Entities/EntityManagerChangeArchetype.cs index 03aad90e..0004e36b 100644 --- a/Unity.Entities/EntityManagerChangeArchetype.cs +++ b/Unity.Entities/EntityManagerChangeArchetype.cs @@ -154,6 +154,7 @@ public bool AddComponent(Entity entity, ComponentType componentType) /// The Entity object. /// The type of component to add. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool AddComponent(Entity entity) { return AddComponent(entity, ComponentType.ReadWrite()); @@ -190,6 +191,38 @@ public void AddComponent(EntityQuery entityQuery, ComponentType componentType) access->AddComponent(queryImpl->_QueryData->MatchingArchetypes, queryImpl->_Filter, componentType); } + + /// + /// Adds components to a set of entities defined by a EntityQuery. + /// + /// + /// Adding a component changes an entity's archetype and results in the entity being moved to a different + /// chunk. + /// + /// The added components have the default values for the type. + /// + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all + /// currently running Jobs to complete before adding the component and no additional Jobs can start before + /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not + /// be able to make use of the processing power of all available cores. + /// + /// The EntityQuery defining the entities to modify. + /// The type of components to add. + [StructuralChangeMethod] + public void AddComponent(EntityQuery entityQuery, ComponentTypes componentTypes) + { + var access = GetCheckedEntityDataAccess(); + var ecs = access->EntityComponentStore; + var queryImpl = entityQuery._GetImpl(); + + Unity.Entities.EntityComponentStore.AssertValidEntityQuery(entityQuery, ecs); + + if (queryImpl->IsEmptyIgnoreFilter) + return; + + access->AddComponents(queryImpl->_QueryData->MatchingArchetypes, queryImpl->_Filter, componentTypes); + } + /// /// Adds a component to a set of entities defines by the EntityQuery and /// sets the component of each entity in the query to the value in the component array. @@ -198,6 +231,7 @@ public void AddComponent(EntityQuery entityQuery, ComponentType componentType) /// THe EntityQuery defining the entities to add component to /// [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER && !NET_DOTS")] public void AddComponentData(EntityQuery entityQuery, NativeArray componentArray) where T : struct, IComponentData { var access = GetCheckedEntityDataAccess(); @@ -207,7 +241,7 @@ public void AddComponentData(EntityQuery entityQuery, NativeArray componen if (entityQuery.IsEmptyIgnoreFilter) return; - using (var entities = entityQuery.ToEntityArray(Allocator.TempJob)) + var entities = entityQuery.ToEntityArray(Allocator.TempJob); { if (entities.Length != componentArray.Length) throw new ArgumentException($"AddComponentData number of entities in query '{entities.Length}' must match componentArray.Length '{componentArray.Length}'."); @@ -218,6 +252,7 @@ public void AddComponentData(EntityQuery entityQuery, NativeArray componen for (int i = 0; i != componentArray.Length; i++) componentData[entities[i]] = componentArray[i]; } + entities.Dispose(); } /// @@ -237,6 +272,7 @@ public void AddComponentData(EntityQuery entityQuery, NativeArray componen /// The EntityQuery defining the entities to modify. /// The type of component to add. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void AddComponent(EntityQuery entityQuery) { AddComponent(entityQuery, ComponentType.ReadWrite()); @@ -262,6 +298,7 @@ public void AddComponent(EntityQuery entityQuery) /// An array of Entity objects. /// The type of component to add. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void AddComponent(NativeArray entities, ComponentType componentType) { var access = GetCheckedEntityDataAccess(); @@ -304,6 +341,7 @@ public void AddComponent(NativeArray entities, ComponentType componentTy } ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); access->PlaybackManagedChanges(); } @@ -358,6 +396,7 @@ public void RemoveComponent(NativeArray entities, ComponentType componen } } ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); access->PlaybackManagedChanges(); } @@ -381,6 +420,7 @@ public void RemoveComponent(NativeArray entities, ComponentType componen /// An array of Entity objects. /// The type of component to add. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void AddComponent(NativeArray entities) { AddComponent(entities, ComponentType.ReadWrite()); @@ -419,6 +459,7 @@ public void AddComponents(Entity entity, ComponentTypes types) ecs->AddComponents(entity, types); ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); access->PlaybackManagedChanges(); } @@ -443,6 +484,40 @@ public bool RemoveComponent(Entity entity, ComponentType componentType) return access->RemoveComponent(entity, componentType); } + /// + /// Removes multiple components from an entity. + /// + /// + /// If the entity has none of the specified components, the call will do nothing. + /// + /// Removing components changes an entity's archetype and results in the entity being moved to a different + /// chunk. + /// + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all + /// currently running Jobs to complete before removing the component and no additional Jobs can start before + /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not + /// be able to make use of the processing power of all available cores. + /// + /// The entity to modify. + /// The types of components to remove. + [StructuralChangeMethod] + public void RemoveComponent(Entity entity, ComponentTypes componentTypes) + { + var access = GetCheckedEntityDataAccess(); + var ecs = access->EntityComponentStore; + + ecs->AssertCanRemoveComponents(entity, componentTypes); + + access->BeforeStructuralChange(); + var archetypeChanges = ecs->BeginArchetypeChangeTracking(); + + ecs->RemoveComponents(entity, componentTypes); + + ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); + access->PlaybackManagedChanges(); + } + /// /// Removes a component from a set of entities defined by a EntityQuery. /// @@ -522,6 +597,7 @@ public void RemoveComponent(EntityQuery entityQuery, ComponentTypes types) /// The entity. /// The type of component to remove. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool RemoveComponent(Entity entity) { return RemoveComponent(entity, ComponentType.ReadWrite()); @@ -542,6 +618,7 @@ public bool RemoveComponent(Entity entity) /// The EntityQuery defining the entities to modify. /// The type of component to remove. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void RemoveComponent(EntityQuery entityQuery) { RemoveComponent(entityQuery, ComponentType.ReadWrite()); @@ -562,6 +639,7 @@ public void RemoveComponent(EntityQuery entityQuery) /// An array identifying the entities to modify. /// The type of component to remove. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void RemoveComponent(NativeArray entities) { RemoveComponent(entities, ComponentType.ReadWrite()); @@ -585,6 +663,7 @@ public void RemoveComponent(NativeArray entities) /// The type of component. /// [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool AddComponentData(Entity entity, T componentData) where T : struct, IComponentData { var type = ComponentType.ReadWrite(); @@ -611,6 +690,7 @@ public bool AddComponentData(Entity entity, T componentData) where T : struct /// The entity. /// The type of component to remove. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool RemoveChunkComponent(Entity entity) { return RemoveComponent(entity, ComponentType.ChunkComponent()); @@ -637,6 +717,7 @@ public bool RemoveChunkComponent(Entity entity) /// The entity. /// The type of component, which must implement IComponentData. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool AddChunkComponentData(Entity entity) where T : struct, IComponentData { return AddComponent(entity, ComponentType.ChunkComponent()); @@ -661,6 +742,7 @@ public bool AddChunkComponentData(Entity entity) where T : struct, IComponent /// The data to set. /// The type of component, which must implement IComponentData. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void AddChunkComponentData(EntityQuery entityQuery, T componentData) where T : unmanaged, IComponentData { var access = GetCheckedEntityDataAccess(); @@ -671,26 +753,37 @@ public void AddChunkComponentData(EntityQuery entityQuery, T componentData) w if (entityQuery.IsEmptyIgnoreFilter) return; - using (var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) + bool validAdd = true; + var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob); + + if (chunks.Length > 0) { - if (chunks.Length == 0) - return; + ecs->CheckCanAddChunkComponent(chunks, ComponentType.ChunkComponent(), ref validAdd); + + if (validAdd) + { + BeforeStructuralChange(); + var archetypeChanges = ecs->BeginArchetypeChangeTracking(); - ecs->AssertCanAddChunkComponent(chunks, ComponentType.ChunkComponent()); + var componentType = ComponentType.ReadWrite(); + var componentTypeIndex = componentType.TypeIndex; + var componentTypeIndexForAdd = TypeManager.MakeChunkComponentTypeIndex(componentTypeIndex); + ArchetypeChunk* chunkPtr = (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks); - BeforeStructuralChange(); - var archetypeChanges = ecs->BeginArchetypeChangeTracking(); + StructuralChange.AddComponentChunks(ecs, chunkPtr, chunks.Length, componentTypeIndexForAdd); + StructuralChange.SetChunkComponent(ecs, chunkPtr, chunks.Length, &componentData, componentTypeIndex); - var componentType = ComponentType.ReadWrite(); - var componentTypeIndex = componentType.TypeIndex; - var componentTypeIndexForAdd = TypeManager.MakeChunkComponentTypeIndex(componentTypeIndex); - ArchetypeChunk* chunkPtr = (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks); + ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); + access->PlaybackManagedChanges(); + } + } - StructuralChange.AddComponentChunks(ecs, chunkPtr, chunks.Length, componentTypeIndexForAdd); - StructuralChange.SetChunkComponent(ecs, chunkPtr, chunks.Length, &componentData, componentTypeIndex); + chunks.Dispose(); - ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); - access->PlaybackManagedChanges(); + if (!validAdd) + { + ecs->ThrowDuplicateChunkComponentError(ComponentType.ChunkComponent()); } } @@ -709,6 +802,7 @@ public void AddChunkComponentData(EntityQuery entityQuery, T componentData) w /// The EntityQuery identifying the chunks to modify. /// The type of component to remove. [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public void RemoveChunkComponentData(EntityQuery entityQuery) { RemoveComponent(entityQuery, ComponentType.ChunkComponent()); @@ -735,6 +829,7 @@ public void RemoveChunkComponentData(EntityQuery entityQuery) /// The buffer. /// [StructuralChangeMethod] + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public DynamicBuffer AddBuffer(Entity entity) where T : struct, IBufferElementData { AddComponent(entity, ComponentType.ReadWrite()); @@ -762,6 +857,7 @@ public DynamicBuffer AddBuffer(Entity entity) where T : struct, IBufferEle /// If the componentData object is not an instance of /// UnityEngine.Component. [StructuralChangeMethod] + [NotBurstCompatible] public void AddComponentObject(Entity entity, object componentData) { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -847,9 +943,30 @@ public void AddSharedComponentData(EntityQuery entityQuery, T componentData) } } + /// + /// Adds and removes components of an entity to match the specified EntityArchetype. + /// + /// + /// Components of the archetype which the entity already has will preserve their values. + /// + /// Components of the archetype which the entity does not have will get the default value for their types. + /// + /// Adding a component to an entity changes its archetype and results in the entity being moved to a + /// different chunk. The entity moves to a chunk with other entities that have the same shared component values. + /// A new chunk is created if no chunk with the same archetype and shared component values currently exists. + /// + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all + /// currently running Jobs to complete before adding the component and no additional Jobs can start before + /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not + /// be able to make use of the processing power of all available cores. + /// + /// The entity whose archetype to change. + /// The new archetype for the entity. [StructuralChangeMethod] public void SetArchetype(Entity entity, EntityArchetype archetype) { + archetype.CheckValidEntityArchetype(); + var access = GetCheckedEntityDataAccess(); var ecs = access->EntityComponentStore; @@ -867,6 +984,7 @@ public void SetArchetype(Entity entity, EntityArchetype archetype) StructuralChange.MoveEntityArchetype(ecs, &entity, archetype.Archetype); ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); access->PlaybackManagedChanges(); } @@ -1004,26 +1122,37 @@ public static void AddChunkComponentData(this EntityManager manager, EntityQu EntityComponentStore.AssertValidEntityQuery(entityQuery, ecs); - using (var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob)) - { - if (chunks.Length == 0) - return; + bool validAdd = true; + var chunks = entityQuery.CreateArchetypeChunkArray(Allocator.TempJob); - ecs->AssertCanAddChunkComponent(chunks, ComponentType.ChunkComponent()); + if (chunks.Length > 0) + { + ecs->CheckCanAddChunkComponent(chunks, ComponentType.ChunkComponent(), ref validAdd); - manager.BeforeStructuralChange(); - var archetypeChanges = ecs->BeginArchetypeChangeTracking(); + if (validAdd) + { + manager.BeforeStructuralChange(); + var archetypeChanges = ecs->BeginArchetypeChangeTracking(); - var type = ComponentType.ReadWrite(); - var chunkType = ComponentType.FromTypeIndex(TypeManager.MakeChunkComponentTypeIndex(type.TypeIndex)); + var type = ComponentType.ReadWrite(); + var chunkType = ComponentType.FromTypeIndex(TypeManager.MakeChunkComponentTypeIndex(type.TypeIndex)); - StructuralChange.AddComponentChunks(ecs, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, chunkType.TypeIndex); + StructuralChange.AddComponentChunks(ecs, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, chunkType.TypeIndex); - ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); - access->PlaybackManagedChanges(); + ecs->EndArchetypeChangeTracking(archetypeChanges, access->EntityQueryManager); + ecs->InvalidateChunkListCacheForChangedArchetypes(); + access->PlaybackManagedChanges(); + } manager.SetChunkComponent(chunks, componentData); } + + chunks.Dispose(); + + if (!validAdd) + { + ecs->ThrowDuplicateChunkComponentError(ComponentType.ChunkComponent()); + } } static void SetChunkComponent(this EntityManager manager, NativeArray chunks, T componentData) where T : class, IComponentData diff --git a/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs b/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs index f03ad3cb..975b07a6 100644 --- a/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs +++ b/Unity.Entities/EntityManagerChangeArchetype.interop.gen.cs @@ -14,6 +14,7 @@ using System; using Unity.Burst; +using Unity.Collections; using System.Runtime.InteropServices; namespace Unity.Entities @@ -67,6 +68,7 @@ private static bool UseDelegate() #endif + [NotBurstCompatible] internal static void Initialize() { #if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) diff --git a/Unity.Entities/EntityManagerCreateArchetype.cs b/Unity.Entities/EntityManagerCreateArchetype.cs index bf7bf5e4..bf2b0e7a 100644 --- a/Unity.Entities/EntityManagerCreateArchetype.cs +++ b/Unity.Entities/EntityManagerCreateArchetype.cs @@ -1,3 +1,5 @@ +using System; +using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Entities @@ -16,8 +18,12 @@ public unsafe partial struct EntityManager /// /// The component types to include as part of the archetype. /// The EntityArchetype object for the archetype. + [NotBurstCompatible] public EntityArchetype CreateArchetype(params ComponentType[] types) { + if (types == null) + throw new NullReferenceException(nameof(types)); + fixed(ComponentType* typesPtr = types) { var access = GetCheckedEntityDataAccess(); diff --git a/Unity.Entities/EntityManagerCreateDestroyEntities.cs b/Unity.Entities/EntityManagerCreateDestroyEntities.cs index 26ecbde2..12aaace5 100644 --- a/Unity.Entities/EntityManagerCreateDestroyEntities.cs +++ b/Unity.Entities/EntityManagerCreateDestroyEntities.cs @@ -30,6 +30,7 @@ public unsafe partial struct EntityManager [StructuralChangeMethod] public Entity CreateEntity(EntityArchetype archetype) { + archetype.CheckValidEntityArchetype(); var access = GetCheckedEntityDataAccess(); return access->CreateEntity(archetype); } @@ -49,11 +50,24 @@ public Entity CreateEntity(EntityArchetype archetype) /// The types of components to add to the new entity. /// The Entity object that you can use to access the entity. [StructuralChangeMethod] + [NotBurstCompatible] public Entity CreateEntity(params ComponentType[] types) { return CreateEntity(CreateArchetype(types)); } + /// + /// Creates an entity with no components. + /// + /// + /// The EntityManager creates the entity in the first available chunk with the archetype having no components. + /// + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all + /// currently running Jobs to complete before creating the entity and no additional Jobs can start before + /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not + /// be able to make use of the processing power of all available cores. + /// + /// The Entity object that you can use to access the entity. [StructuralChangeMethod] public Entity CreateEntity() { @@ -81,6 +95,7 @@ public Entity CreateEntity() [StructuralChangeMethod] public void CreateEntity(EntityArchetype archetype, NativeArray entities) { + archetype.CheckValidEntityArchetype(); GetCheckedEntityDataAccess()->CreateEntity(archetype, entities); } @@ -100,6 +115,7 @@ public void CreateEntity(EntityArchetype archetype, NativeArray entities [StructuralChangeMethod] public NativeArray CreateEntity(EntityArchetype archetype, int entityCount, Allocator allocator) { + archetype.CheckValidEntityArchetype(); var entities = new NativeArray(entityCount, allocator); GetCheckedEntityDataAccess()->CreateEntity(archetype, entities); @@ -181,7 +197,6 @@ public void DestroyEntity(Entity entity) /// If the source entity was converted from a prefab and thus has a component, /// the entire group is cloned as a new set of entities. Entity references on components that are being cloned to entities inside /// the set are remapped to the instantiated entities. - /// /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the entity and no additional Jobs can start before /// the function is finished. A sync point can cause a drop in performance because the ECS framework may not diff --git a/Unity.Entities/EntityManagerDebug.cs b/Unity.Entities/EntityManagerDebug.cs index a43bc61e..a40ef9ef 100644 --- a/Unity.Entities/EntityManagerDebug.cs +++ b/Unity.Entities/EntityManagerDebug.cs @@ -72,6 +72,28 @@ public NativeArray GetAllEntities(Allocator allocator = Allocator.Temp) return array; } + internal NativeArray GetAllEntitiesImmediate(Allocator allocator = Allocator.Temp) + { + BeforeStructuralChange(); + + var chunks = GetAllChunksImmediate(Allocator.TempJob); + var count = ArchetypeChunkArray.CalculateEntityCount(chunks); + var array = new NativeArray(count, allocator); + var entityType = GetEntityTypeHandle(); + var offset = 0; + + for (int i = 0; i < chunks.Length; i++) + { + var chunk = chunks[i]; + var entities = chunk.GetNativeArray(entityType); + array.Slice(offset, entities.Length).CopyFrom(entities); + offset += entities.Length; + } + + chunks.Dispose(); + return array; + } + // @TODO document EntityManagerDebug /// /// Provides information and utility functions for debugging. diff --git a/Unity.Entities/EntityManagerExclusiveEntityTransaction.cs b/Unity.Entities/EntityManagerExclusiveEntityTransaction.cs index 86e46db3..b902bd1a 100644 --- a/Unity.Entities/EntityManagerExclusiveEntityTransaction.cs +++ b/Unity.Entities/EntityManagerExclusiveEntityTransaction.cs @@ -104,6 +104,7 @@ internal void AllocateConsecutiveEntitiesForLoading(int count) s->AllocateConsecutiveEntitiesForLoading(count); } + [NotBurstCompatible] internal void AddSharedComponent(NativeArray chunks, T componentData) where T : struct, ISharedComponentData { diff --git a/Unity.Entities/EntityManagerMoveEntities.cs b/Unity.Entities/EntityManagerMoveEntities.cs index 58eddf6f..0a04e991 100644 --- a/Unity.Entities/EntityManagerMoveEntities.cs +++ b/Unity.Entities/EntityManagerMoveEntities.cs @@ -277,6 +277,8 @@ void MoveEntitiesFromInternalQuery(EntityManager srcEntities, EntityQuery filter MoveChunksFromFiltered(chunks, entityRemapping, srcAccess->EntityComponentStore, srcAccess->ManagedComponentStore); selfAccess->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, selfAccess->EntityQueryManager); + selfAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); + srcAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); } } @@ -305,6 +307,8 @@ public void MoveEntitiesFromInternalAll(EntityManager srcEntities, NativeArrayEntityComponentStore, srcAccess->ManagedComponentStore); selfAccess->EntityComponentStore->EndArchetypeChangeTracking(archetypeChanges, selfAccess->EntityQueryManager); + selfAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); + srcAccess->EntityComponentStore->InvalidateChunkListCacheForChangedArchetypes(); } [NotBurstCompatible] @@ -503,6 +507,9 @@ internal void MoveChunksFromAll( remapArchetypes[archetypeIndex] = new RemapArchetype {srcArchetype = srcArchetype, dstArchetype = dstArchetype}; + srcEntityComponentStore->m_ChunkListChangesTracker.TrackArchetype(srcArchetype); + ecs->m_ChunkListChangesTracker.TrackArchetype(srcArchetype); + for (var j = 0; j < srcArchetype->Chunks.Count; ++j) { var srcChunk = srcArchetype->Chunks[j]; @@ -580,7 +587,7 @@ internal struct RemapArchetype } [BurstCompile] - internal struct ChunkPatchEntities : IJobBurstScheduable + internal struct ChunkPatchEntities : IJobBurstSchedulable { public NativeArray RemapChunks; public NativeArray EntityRemapping; @@ -600,7 +607,7 @@ public void Execute() } [BurstCompile] - internal struct MoveChunksJob : IJobBurstScheduable + internal struct MoveChunksJob : IJobBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* srcEntityComponentStore; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* dstEntityComponentStore; @@ -620,7 +627,7 @@ public void Execute() } [BurstCompile] - internal struct GatherAllManagedComponentIndicesJob : IJobBurstScheduable + internal struct GatherAllManagedComponentIndicesJob : IJobBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* SrcEntityComponentStore; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* DstEntityComponentStore; @@ -661,7 +668,7 @@ public void Execute() } [BurstCompile] - struct GatherManagedComponentIndicesInChunkJob : IJobBurstScheduable + struct GatherManagedComponentIndicesInChunkJob : IJobBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* SrcEntityComponentStore; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* DstEntityComponentStore; @@ -707,7 +714,7 @@ public void Execute() } [BurstCompile] - struct RemapChunksFilteredJob : IJobParallelForBurstScheduable + struct RemapChunksFilteredJob : IJobParallelForBurstSchedulable { [ReadOnly] public NativeArray entityRemapping; [ReadOnly] public NativeArray remapChunks; @@ -745,7 +752,7 @@ public void Execute(int index) } [BurstCompile] - struct MoveFilteredChunksBetweenArchetypexJob : IJobBurstScheduable + struct MoveFilteredChunksBetweenArchetypexJob : IJobBurstSchedulable { [ReadOnly] public NativeArray RemapChunks; [ReadOnly] public NativeArray RemapShared; @@ -775,7 +782,7 @@ public void Execute() } [BurstCompile] - struct RemapAllChunksJob : IJobParallelForBurstScheduable + struct RemapAllChunksJob : IJobParallelForBurstSchedulable { [ReadOnly] public NativeArray entityRemapping; [ReadOnly] public NativeArray remapChunks; @@ -799,7 +806,7 @@ public void Execute(int index) } [BurstCompile] - struct RemapAllArchetypesJob : IJobParallelForBurstScheduable + struct RemapAllArchetypesJob : IJobParallelForBurstSchedulable { [DeallocateOnJobCompletion][ReadOnly] public NativeArray remapArchetypes; @@ -898,7 +905,7 @@ public void Execute(int index) } [BurstCompile] - struct MoveAllChunksJob : IJobBurstScheduable + struct MoveAllChunksJob : IJobBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* srcEntityComponentStore; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* dstEntityComponentStore; diff --git a/Unity.Entities/EntityManagerQuery.cs b/Unity.Entities/EntityManagerQuery.cs index d5aa9d7b..48658983 100644 --- a/Unity.Entities/EntityManagerQuery.cs +++ b/Unity.Entities/EntityManagerQuery.cs @@ -57,6 +57,13 @@ public NativeArray GetAllChunks(Allocator allocator = Allocator. return access->m_UniversalQuery.CreateArchetypeChunkArray(allocator); } + internal NativeArray GetAllChunksImmediate(Allocator allocator) + { + var access = GetCheckedEntityDataAccess(); + access->BeforeStructuralChange(); + return access->m_UniversalQuery.CreateArchetypeChunkArrayImmediate(allocator); + } + /// /// Gets all the archetypes. /// diff --git a/Unity.Entities/EntityManagerSerialization.cs b/Unity.Entities/EntityManagerSerialization.cs index e636e6ce..4704ddd0 100644 --- a/Unity.Entities/EntityManagerSerialization.cs +++ b/Unity.Entities/EntityManagerSerialization.cs @@ -1,4 +1,5 @@ using Unity.Assertions; +using Unity.Collections; namespace Unity.Entities { diff --git a/Unity.Entities/EntityManagerTypes.cs b/Unity.Entities/EntityManagerTypes.cs index 4d7d5475..fee22769 100644 --- a/Unity.Entities/EntityManagerTypes.cs +++ b/Unity.Entities/EntityManagerTypes.cs @@ -23,6 +23,7 @@ public unsafe partial struct EntityManager /// or read and write. For managed components isReadonly will always be treated as false. /// The compile-time type of the component. /// The run-time type information of the component. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public ComponentTypeHandle GetComponentTypeHandle(bool isReadOnly) { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -72,6 +73,7 @@ public DynamicComponentTypeHandle GetDynamicComponentTypeHandle(ComponentType co /// or read and write. /// The compile-time type of the buffer elements. /// The run-time type information of the buffer component. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public BufferTypeHandle GetBufferTypeHandle(bool isReadOnly) where T : struct, IBufferElementData { @@ -89,6 +91,7 @@ public BufferTypeHandle GetBufferTypeHandle(bool isReadOnly) /// /// The compile-time type of the shared component. /// The run-time type information of the shared component. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleSharedComponentData) })] public SharedComponentTypeHandle GetSharedComponentTypeHandle() where T : struct, ISharedComponentData { @@ -149,6 +152,7 @@ public NativeArray GetComponentTypes(Entity entity, Allocator all /// The type to check. /// The list to receive the output. /// The list that was passed in, containing the System.Types that can be assigned to `interfaceType`. + [NotBurstCompatible] public List GetAssignableComponentTypes(Type interfaceType, List listOut) { // #todo Cache this. It only can change when TypeManager.GetTypeCount() changes @@ -169,6 +173,7 @@ public List GetAssignableComponentTypes(Type interfaceType, List lis /// inherit from the same compile-time type. /// The type to check. /// A new List object containing the System.Types that can be assigned to `interfaceType`. + [NotBurstCompatible] public List GetAssignableComponentTypes(Type interfaceType) => GetAssignableComponentTypes(interfaceType, new List()); diff --git a/Unity.Entities/EntityManagerValidate.cs b/Unity.Entities/EntityManagerValidate.cs index e8a3d39e..e4504866 100644 --- a/Unity.Entities/EntityManagerValidate.cs +++ b/Unity.Entities/EntityManagerValidate.cs @@ -1,3 +1,5 @@ +using Unity.Collections; + namespace Unity.Entities { public unsafe partial struct EntityManager @@ -36,6 +38,7 @@ public bool Exists(Entity entity) /// The Entity object. /// The data type of the component. /// True, if the specified entity has the component. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool HasComponent(Entity entity) { return GetCheckedEntityDataAccess()->HasComponent(entity, ComponentType.ReadWrite()); @@ -60,6 +63,7 @@ public bool HasComponent(Entity entity, ComponentType type) /// The Entity object. /// The data type of the chunk component. /// True, if the chunk containing the specified entity has the component. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool HasChunkComponent(Entity entity) { return GetCheckedEntityDataAccess()->HasComponent(entity, ComponentType.ChunkComponent()); diff --git a/Unity.Entities/EntityManagerVersions.cs b/Unity.Entities/EntityManagerVersions.cs index 0daf017e..f1c42b86 100644 --- a/Unity.Entities/EntityManagerVersions.cs +++ b/Unity.Entities/EntityManagerVersions.cs @@ -1,4 +1,5 @@ using System; +using Unity.Collections; namespace Unity.Entities { @@ -24,6 +25,7 @@ public unsafe partial struct EntityManager /// /// The component type. /// The current version number. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public int GetComponentOrderVersion() { var access = GetCheckedEntityDataAccess(); @@ -48,6 +50,7 @@ public int GetComponentOrderVersion() /// The shared component instance. /// The shared component type. /// The current version number. + [NotBurstCompatible] public int GetSharedComponentOrderVersion(T sharedComponent) where T : struct, ISharedComponentData { var access = GetCheckedEntityDataAccess(); diff --git a/Unity.Entities/IJobBurstScheduable.cs b/Unity.Entities/IJobBurstSchedulable.cs similarity index 86% rename from Unity.Entities/IJobBurstScheduable.cs rename to Unity.Entities/IJobBurstSchedulable.cs index 3e07721b..38ef06f9 100644 --- a/Unity.Entities/IJobBurstScheduable.cs +++ b/Unity.Entities/IJobBurstSchedulable.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Burst; using Unity.Jobs.LowLevel.Unsafe; @@ -9,15 +8,15 @@ namespace Unity.Entities { - [JobProducerType(typeof(IJobBurstScheduableExtensions.JobStruct<>))] - internal interface IJobBurstScheduable + [JobProducerType(typeof(IJobBurstSchedulableExtensions.JobStruct<>))] + internal interface IJobBurstSchedulable { void Execute(); } - internal static class IJobBurstScheduableExtensions + internal static class IJobBurstSchedulableExtensions { - internal struct JobStruct where T : struct, IJobBurstScheduable + internal struct JobStruct where T : struct, IJobBurstSchedulable { public static readonly SharedStatic jobReflectionData = SharedStatic.GetOrCreate>(); @@ -26,7 +25,7 @@ public static void Initialize() { if (jobReflectionData.Data == IntPtr.Zero) { -#if UNITY_2020_2_OR_NEWER +#if UNITY_2020_2_OR_NEWER || UNITY_DOTSRUNTIME jobReflectionData.Data = JobsUtility.CreateJobReflectionData(typeof(T), (ExecuteJobFunction)Execute); #else jobReflectionData.Data = JobsUtility.CreateJobReflectionData(typeof(T), JobType.Single, (ExecuteJobFunction)Execute); @@ -49,11 +48,11 @@ private static void CheckReflectionDataCorrect(IntPtr reflectionData) throw new InvalidOperationException("Reflection data was not set up by an Initialize() call"); } - unsafe internal static JobHandle Schedule(this T jobData, JobHandle dependsOn = new JobHandle()) where T : struct, IJobBurstScheduable + unsafe internal static JobHandle Schedule(this T jobData, JobHandle dependsOn = new JobHandle()) where T : struct, IJobBurstSchedulable { var reflectionData = JobStruct.jobReflectionData.Data; CheckReflectionDataCorrect(reflectionData); -#if UNITY_2020_2_OR_NEWER +#if UNITY_2020_2_OR_NEWER || UNITY_DOTSRUNTIME var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), reflectionData, dependsOn, ScheduleMode.Parallel); #else var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), reflectionData, dependsOn, ScheduleMode.Batched); @@ -61,7 +60,7 @@ private static void CheckReflectionDataCorrect(IntPtr reflectionData) return JobsUtility.Schedule(ref scheduleParams); } - unsafe internal static void Run(this T jobData) where T : struct, IJobBurstScheduable + unsafe internal static void Run(this T jobData) where T : struct, IJobBurstSchedulable { var reflectionData = JobStruct.jobReflectionData.Data; CheckReflectionDataCorrect(reflectionData); @@ -70,9 +69,3 @@ unsafe internal static void Run(this T jobData) where T : struct, IJobBurstSc } } } -#else -namespace Unity.Jobs -{ - internal interface IJobBurstScheduable : IJob {} -} -#endif diff --git a/Unity.Entities/IJobBurstScheduable.cs.meta b/Unity.Entities/IJobBurstSchedulable.cs.meta similarity index 100% rename from Unity.Entities/IJobBurstScheduable.cs.meta rename to Unity.Entities/IJobBurstSchedulable.cs.meta diff --git a/Unity.Entities/IJobChunk.cs b/Unity.Entities/IJobChunk.cs index 8a5f1e58..90944dd3 100644 --- a/Unity.Entities/IJobChunk.cs +++ b/Unity.Entities/IJobChunk.cs @@ -64,18 +64,31 @@ internal struct JobChunkWrapper where T : struct { #if ENABLE_UNITY_COLLECTIONS_CHECKS #pragma warning disable 414 - [ReadOnly] public EntitySafetyHandle safety; + [ReadOnly] internal EntitySafetyHandle safety; #pragma warning restore #endif - public T JobData; + internal T JobData; [NativeDisableContainerSafetyRestriction] [DeallocateOnJobCompletion] - public NativeArray PrefilterData; + internal NativeArray PrefilterData; - public int IsParallel; + internal int IsParallel; } +#if UNITY_2020_2_OR_NEWER && !UNITY_DOTSRUNTIME + /// + /// This method is only to be called by automatically generated setup code. + /// + /// + public static void EarlyJobInit() + where T : struct, IJobChunk + { + JobChunkProducer.CreateReflectionData(); + } +#endif + + /// /// Adds an IJobChunk instance to the Job scheduler queue for parallel execution. /// Note: This method is being replaced with use of ScheduleParallel to make non-sequential execution explicit. @@ -190,7 +203,7 @@ internal static unsafe JobHandle ScheduleInternal(ref T jobData, EntityQuery #if ENABLE_UNITY_COLLECTIONS_CHECKS // All IJobChunk jobs have a EntityManager safety handle to ensure that BeforeStructuralChange throws an error if // jobs without any other safety handles are still running (haven't been synced). - safety = new EntitySafetyHandle {m_Safety = impl->_Access->DependencyManager->Safety.GetEntityManagerSafetyHandle()}, + safety = new EntitySafetyHandle { m_Safety = impl->_Access->DependencyManager->Safety.GetEntityManagerSafetyHandle() }, #endif JobData = jobData, @@ -204,10 +217,62 @@ internal static unsafe JobHandle ScheduleInternal(ref T jobData, EntityQuery prefilterHandle, mode); -#if ENABLE_UNITY_COLLECTIONS_CHECKS +#if UNITY_DOTSRUNTIME try { + if (!isParallel) + { + return JobsUtility.Schedule(ref scheduleParams); + } + else + { + if (useFiltering) + return JobsUtility.ScheduleParallelForDeferArraySize(ref scheduleParams, 1, deferredCountData, null); + else + return JobsUtility.ScheduleParallelFor(ref scheduleParams, unfilteredChunkCount, 1); + } + } + catch (InvalidOperationException e) + { + prefilterHandle.Complete(); + prefilterData.Dispose(); + throw e; + } +#else + // We can't use try {} catch {} with 2020.2 as we will be burst compiling the schedule code. + // Burst doesn't support exception handling. + bool executedManaged = false; + JobHandle result = default; + FinalizeScheduleChecked(isParallel, unfilteredChunkCount, prefilterHandle, prefilterData, deferredCountData, useFiltering, ref scheduleParams, ref executedManaged, ref result); + + if (executedManaged) + return result; + + return FinalizeScheduleNoExceptions(isParallel, unfilteredChunkCount, deferredCountData, useFiltering, ref scheduleParams); #endif + } + +#if !UNITY_DOTSRUNTIME + // Burst does not support exception handling. + [BurstDiscard] + private static unsafe void FinalizeScheduleChecked(bool isParallel, int unfilteredChunkCount, JobHandle prefilterHandle, NativeArray prefilterData, void* deferredCountData, bool useFiltering, ref JobsUtility.JobScheduleParameters scheduleParams, ref bool executed, ref JobHandle result) + { + executed = true; + + try + { + result = FinalizeScheduleNoExceptions(isParallel, unfilteredChunkCount, deferredCountData, useFiltering, ref scheduleParams); + } + catch (InvalidOperationException e) + { + prefilterHandle.Complete(); + prefilterData.Dispose(); + throw e; + } + } + + private static unsafe JobHandle FinalizeScheduleNoExceptions(bool isParallel, int unfilteredChunkCount, void* deferredCountData, bool useFiltering, ref JobsUtility.JobScheduleParameters scheduleParams) + { if (!isParallel) { return JobsUtility.Schedule(ref scheduleParams); @@ -219,55 +284,55 @@ internal static unsafe JobHandle ScheduleInternal(ref T jobData, EntityQuery else return JobsUtility.ScheduleParallelFor(ref scheduleParams, unfilteredChunkCount, 1); } -#if ENABLE_UNITY_COLLECTIONS_CHECKS - } - - catch (InvalidOperationException e) - { - prefilterHandle.Complete(); - prefilterData.Dispose(); - throw e; } #endif - } internal struct JobChunkProducer where T : struct, IJobChunk { - static IntPtr s_JobReflectionDataParallel; - static IntPtr s_JobReflectionDataSingle; - public static IntPtr InitializeSingle() +#if UNITY_2020_2_OR_NEWER && !UNITY_DOTSRUNTIME + internal static readonly SharedStatic s_ReflectionData = SharedStatic.GetOrCreate(); + + internal static void CreateReflectionData() { - if (s_JobReflectionDataSingle == IntPtr.Zero) -#if UNITY_2020_2_OR_NEWER - s_JobReflectionDataSingle = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), - typeof(T), (ExecuteJobFunction)Execute); + s_ReflectionData.Data = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), typeof(T), (ExecuteJobFunction)Execute); + } #else - s_JobReflectionDataSingle = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), - typeof(T), JobType.Single, (ExecuteJobFunction)Execute); + static IntPtr s_JobReflectionDataParallel; + static IntPtr s_JobReflectionDataSingle; #endif + internal static IntPtr InitializeSingle() + { +#if UNITY_2020_2_OR_NEWER && !UNITY_DOTSRUNTIME + return InitializeParallel(); +#else + if (s_JobReflectionDataSingle == IntPtr.Zero) + s_JobReflectionDataSingle = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), typeof(T), JobType.Single, (ExecuteJobFunction)Execute); return s_JobReflectionDataSingle; +#endif } - public static IntPtr InitializeParallel() + internal static IntPtr InitializeParallel() { - if (s_JobReflectionDataParallel == IntPtr.Zero) -#if UNITY_2020_2_OR_NEWER - s_JobReflectionDataParallel = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), - typeof(T), (ExecuteJobFunction)Execute); -#else - s_JobReflectionDataParallel = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), - typeof(T), JobType.ParallelFor, (ExecuteJobFunction)Execute); +#if UNITY_2020_2_OR_NEWER && !UNITY_DOTSRUNTIME + IntPtr result = s_ReflectionData.Data; +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (result == IntPtr.Zero) + throw new InvalidOperationException("IJobChunk job reflection data has not been automatically computed - this is a bug"); #endif - + return result; +#else + if (s_JobReflectionDataParallel == IntPtr.Zero) + s_JobReflectionDataParallel = JobsUtility.CreateJobReflectionData(typeof(JobChunkWrapper), typeof(T), JobType.ParallelFor, (ExecuteJobFunction)Execute); return s_JobReflectionDataParallel; +#endif } - public delegate void ExecuteJobFunction(ref JobChunkWrapper jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex); + internal delegate void ExecuteJobFunction(ref JobChunkWrapper jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex); - public static void Execute(ref JobChunkWrapper jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) + internal static void Execute(ref JobChunkWrapper jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref JobRanges ranges, int jobIndex) { ExecuteInternal(ref jobWrapper, ref ranges, jobIndex); } diff --git a/Unity.Entities/IJobEntityBatch.cs b/Unity.Entities/IJobEntityBatch.cs index 047cadb9..c650a613 100644 --- a/Unity.Entities/IJobEntityBatch.cs +++ b/Unity.Entities/IJobEntityBatch.cs @@ -23,7 +23,7 @@ namespace Unity.Entities /// struct's `Execute` function is called for each batch. /// /// When you schedule or run the job with one of the following methods: - /// * , + /// * , /// * , /// * or /// @@ -95,7 +95,7 @@ public interface IJobEntityBatch /// struct's `Execute` function is called for each batch. /// /// When you schedule or run the job with one of the following methods: - /// * , + /// * , /// * , /// * or /// @@ -154,7 +154,7 @@ public interface IJobEntityBatchWithIndex void Execute(ArchetypeChunk batchInChunk, int batchIndex, int indexOfFirstEntityInQuery); } - /// + /// /// Extensions for scheduling and running jobs. /// public static class JobEntityBatchExtensions @@ -175,9 +175,9 @@ internal struct JobEntityBatchWrapper where T : struct #endif public T JobData; - [DeallocateOnJobCompletion] - [NativeDisableContainerSafetyRestriction] - public NativeArray Batches; + public UnsafeMatchingArchetypePtrList MatchingArchetypes; + public UnsafeCachedChunkList CachedChunks; + public EntityQueryFilter Filter; public int JobsPerChunk; public int IsParallel; @@ -196,6 +196,33 @@ internal struct JobEntityBatchWrapper where T : struct /// The specific implementation type. /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` /// parameter. + public static unsafe JobHandle Schedule( + this T jobData, + EntityQuery query, + JobHandle dependsOn = default(JobHandle)) + where T : struct, IJobEntityBatch + { +#if UNITY_2020_2_OR_NEWER + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Single, 1, false); +#else + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, false); +#endif + } + + /// + /// Adds an instance to the job scheduler queue for sequential (non-parallel) execution. + /// + /// This scheduling variant processes each matching chunk as a single batch. All chunks execute + /// sequentially. + /// An instance. + /// The query selecting chunks with the necessary components. + /// The handle identifying already scheduled jobs that could constrain this job. + /// A job that writes to a component cannot run in parallel with other jobs that read or write that component. + /// Jobs that only read the same components can run in parallel. + /// The specific implementation type. + /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` + /// parameter. + [Obsolete("ScheduleSingle has been renamed to Schedule. (RemovedAfter 2020-10-09). (UnityUpgradable) -> Schedule(*)", false)] public static unsafe JobHandle ScheduleSingle( this T jobData, EntityQuery query, @@ -203,7 +230,7 @@ public static unsafe JobHandle ScheduleSingle( where T : struct, IJobEntityBatch { #if UNITY_2020_2_OR_NEWER - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, 1, false); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Single, 1, false); #else return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, false); #endif @@ -227,13 +254,14 @@ public static unsafe JobHandle ScheduleSingle( public static unsafe JobHandle ScheduleParallel( this T jobData, EntityQuery query, + int batchesPerChunk = 1, JobHandle dependsOn = default(JobHandle)) where T : struct, IJobEntityBatch { #if UNITY_2020_2_OR_NEWER - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, 1, true); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, batchesPerChunk, true); #else - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, true); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, batchesPerChunk, true); #endif } @@ -254,6 +282,7 @@ public static unsafe JobHandle ScheduleParallel( /// The specific implementation type. /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` /// parameter. + [Obsolete("ScheduleParallelBatched is being deprecated. Use ScheduleParallel instead. (RemovedAfter 2020-10-09). (UnityUpgradable) -> ScheduleParallel(*)", false)] public static unsafe JobHandle ScheduleParallelBatched( this T jobData, EntityQuery query, @@ -292,17 +321,12 @@ internal static unsafe JobHandle ScheduleInternal( where T : struct, IJobEntityBatch { var queryImpl = query._GetImpl(); - var filteredChunkCount = queryImpl->CalculateChunkCount(); - var batches = new NativeArray(filteredChunkCount * batchesPerChunk, Allocator.TempJob); + var queryData = queryImpl->_QueryData; - var prefilterHandle = new PrefilterForJobEntityBatch - { - MatchingArchetypes = queryImpl->_QueryData->MatchingArchetypes, - Filter = queryImpl->_Filter, - BatchesPerChunk = batchesPerChunk, - EntityComponentStore = queryImpl->_Access->EntityComponentStore, - Batches = batches - }.Schedule(dependsOn); + var cachedChunks = queryData->GetMatchingChunkCache(); + + // Don't schedule the job if there are no chunks to work on + var chunkCount = cachedChunks.Length; JobEntityBatchWrapper jobEntityBatchWrapper = new JobEntityBatchWrapper { @@ -312,9 +336,11 @@ internal static unsafe JobHandle ScheduleInternal( safety = new EntitySafetyHandle {m_Safety = queryImpl->SafetyHandles->GetEntityManagerSafetyHandle()}, #endif - JobData = jobData, - Batches = batches, + MatchingArchetypes = queryData->MatchingArchetypes, + CachedChunks = cachedChunks, + Filter = queryImpl->_Filter, + JobData = jobData, JobsPerChunk = batchesPerChunk, IsParallel = isParallel ? 1 : 0 }; @@ -324,31 +350,17 @@ internal static unsafe JobHandle ScheduleInternal( isParallel ? JobEntityBatchProducer.InitializeParallel() : JobEntityBatchProducer.InitializeSingle(), - prefilterHandle, + dependsOn, mode); -#if ENABLE_UNITY_COLLECTIONS_CHECKS - try - { -#endif if (!isParallel) { return JobsUtility.Schedule(ref scheduleParams); } else { - return JobsUtility.ScheduleParallelFor(ref scheduleParams, filteredChunkCount * batchesPerChunk, 1); + return JobsUtility.ScheduleParallelFor(ref scheduleParams, chunkCount * batchesPerChunk, 1); } -#if ENABLE_UNITY_COLLECTIONS_CHECKS - } - - catch (InvalidOperationException e) - { - prefilterHandle.Complete(); - batches.Dispose(); - throw e; - } -#endif } internal struct JobEntityBatchProducer @@ -407,13 +419,14 @@ internal unsafe static void ExecuteInternal( ref JobRanges ranges, int jobIndex) { - var batches = jobWrapper.Batches; + var chunks = jobWrapper.CachedChunks; bool isParallel = jobWrapper.IsParallel == 1; + bool isFiltering = jobWrapper.Filter.RequiresMatchesFilter; while (true) { int beginBatchIndex = 0; - int endBatchIndex = batches.Length; + int endBatchIndex = chunks.Length; // If we are running the job in parallel, steal some work. if (isParallel) @@ -426,7 +439,14 @@ internal unsafe static void ExecuteInternal( // Do the actual user work. for (int batchIndex = beginBatchIndex; batchIndex < endBatchIndex; ++batchIndex) { - jobWrapper.JobData.Execute(batches[batchIndex], batchIndex); + var chunkIndex = batchIndex / jobWrapper.JobsPerChunk; + var batchIndexInChunk = batchIndex % jobWrapper.JobsPerChunk; + var chunk = chunks.Ptr[chunkIndex]; + + if (isFiltering && chunk->MatchesFilter(jobWrapper.MatchingArchetypes.Ptr[chunks.PerChunkMatchingArchetypeIndex.Ptr[chunkIndex]], ref jobWrapper.Filter)) + continue; + + jobWrapper.JobData.Execute(ArchetypeChunk.EntityBatchFromChunk(chunk, jobWrapper.JobsPerChunk, batchIndexInChunk, chunks.EntityComponentStore), batchIndex); } // If we are not running in parallel, our job is done. @@ -479,6 +499,33 @@ internal struct JobEntityBatchIndexWrapper where T : struct /// The specific implementation type. /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` /// parameter. + public static unsafe JobHandle Schedule( + this T jobData, + EntityQuery query, + JobHandle dependsOn = default(JobHandle)) + where T : struct, IJobEntityBatchWithIndex + { +#if UNITY_2020_2_OR_NEWER + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Single, 1, false); +#else + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, false); +#endif + } + + /// + /// Adds an instance to the job scheduler queue for sequential (non-parallel) execution. + /// + /// This scheduling variant processes each matching chunk as a single batch. All chunks execute + /// sequentially. + /// An instance. + /// The query selecting chunks with the necessary components. + /// The handle identifying already scheduled jobs that could constrain this job. + /// A job that writes to a component cannot run in parallel with other jobs that read or write that component. + /// Jobs that only read the same components can run in parallel. + /// The specific implementation type. + /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` + /// parameter. + [Obsolete("ScheduleSingle has been renamed to Schedule. (RemovedAfter 2020-10-09). (UnityUpgradable) -> Schedule(*)", false)] public static unsafe JobHandle ScheduleSingle( this T jobData, EntityQuery query, @@ -486,7 +533,7 @@ public static unsafe JobHandle ScheduleSingle( where T : struct, IJobEntityBatchWithIndex { #if UNITY_2020_2_OR_NEWER - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, 1, false); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Single, 1, false); #else return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, false); #endif @@ -510,13 +557,14 @@ public static unsafe JobHandle ScheduleSingle( public static unsafe JobHandle ScheduleParallel( this T jobData, EntityQuery query, + int batchesPerChunk = 1, JobHandle dependsOn = default(JobHandle)) where T : struct, IJobEntityBatchWithIndex { #if UNITY_2020_2_OR_NEWER - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, 1, true); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Parallel, batchesPerChunk, true); #else - return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, 1, true); + return ScheduleInternal(ref jobData, query, dependsOn, ScheduleMode.Batched, batchesPerChunk, true); #endif } @@ -537,6 +585,7 @@ public static unsafe JobHandle ScheduleParallel( /// The specific implementation type. /// A handle that combines the current Job with previous dependencies identified by the `dependsOn` /// parameter. + [Obsolete("ScheduleParallelBatched is being deprecated. Use ScheduleParallel instead. (RemovedAfter 2020-10-09). (UnityUpgradable) -> ScheduleParallel(*)", false)] public static unsafe JobHandle ScheduleParallelBatched( this T jobData, EntityQuery query, @@ -733,42 +782,6 @@ internal unsafe static void ExecuteInternal( } } - [BurstCompile] - unsafe struct PrefilterForJobEntityBatch : IJob - { - [NativeDisableUnsafePtrRestriction] public UnsafeMatchingArchetypePtrList MatchingArchetypes; - public EntityQueryFilter Filter; - public int BatchesPerChunk; - [NativeDisableUnsafePtrRestriction] public EntityComponentStore* EntityComponentStore; - - [NativeDisableParallelForRestriction] public NativeArray Batches; - - public void Execute() - { - var batchCounter = 0; - - for (var m = 0; m < MatchingArchetypes.Length; ++m) - { - var match = MatchingArchetypes.Ptr[m]; - if (match->Archetype->EntityCount <= 0) - continue; - - var archetype = match->Archetype; - int chunkCount = archetype->Chunks.Count; - - for (int chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) - { - var chunk = archetype->Chunks[chunkIndex]; - for (int batchIndex = 0; batchIndex < BatchesPerChunk; ++batchIndex) - { - if (match->ChunkMatchesFilter(chunkIndex, ref Filter)) - Batches[batchCounter++] = ArchetypeChunk.EntityBatchFromChunk(chunk, BatchesPerChunk, batchIndex, EntityComponentStore); - } - } - } - } - } - [BurstCompile] unsafe struct PrefilterForJobEntityBatchWithIndex : IJob { diff --git a/Unity.Entities/IJobParallelForBurstScheduable.cs b/Unity.Entities/IJobParallelForBurstSchedulable.cs similarity index 85% rename from Unity.Entities/IJobParallelForBurstScheduable.cs rename to Unity.Entities/IJobParallelForBurstSchedulable.cs index de4de8a6..02129b60 100644 --- a/Unity.Entities/IJobParallelForBurstScheduable.cs +++ b/Unity.Entities/IJobParallelForBurstSchedulable.cs @@ -7,24 +7,23 @@ namespace Unity.Entities { -#if !UNITY_DOTSRUNTIME - [JobProducerType(typeof(IJobParallelForExtensionsBurstScheduable.ParallelForJobStructBurstScheduable<>))] - internal interface IJobParallelForBurstScheduable + [JobProducerType(typeof(IJobParallelForExtensionsBurstSchedulable.ParallelForJobStructBurstSchedulable<>))] + internal interface IJobParallelForBurstSchedulable { void Execute(int index); } - internal static class IJobParallelForExtensionsBurstScheduable + internal static class IJobParallelForExtensionsBurstSchedulable { - internal struct ParallelForJobStructBurstScheduable where T : struct, IJobParallelForBurstScheduable + internal struct ParallelForJobStructBurstSchedulable where T : struct, IJobParallelForBurstSchedulable { - internal static readonly SharedStatic jobReflectionData = SharedStatic.GetOrCreate>(); + internal static readonly SharedStatic jobReflectionData = SharedStatic.GetOrCreate>(); internal static void Initialize() { if (jobReflectionData.Data == IntPtr.Zero) { -#if UNITY_2020_2_OR_NEWER +#if UNITY_2020_2_OR_NEWER || UNITY_DOTSRUNTIME jobReflectionData.Data = JobsUtility.CreateJobReflectionData(typeof(T), (ExecuteJobFunction)Execute); #else jobReflectionData.Data = JobsUtility.CreateJobReflectionData(typeof(T), JobType.ParallelFor, (ExecuteJobFunction)Execute); @@ -61,11 +60,11 @@ private static void CheckReflectionDataCorrect(IntPtr reflectionData) throw new InvalidOperationException("Reflection data was not set up by a call to Initialize()"); } - unsafe internal static JobHandle Schedule(this T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = new JobHandle()) where T : struct, IJobParallelForBurstScheduable + unsafe internal static JobHandle Schedule(this T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = new JobHandle()) where T : struct, IJobParallelForBurstSchedulable { - var reflectionData = ParallelForJobStructBurstScheduable.jobReflectionData.Data; + var reflectionData = ParallelForJobStructBurstSchedulable.jobReflectionData.Data; CheckReflectionDataCorrect(reflectionData); -#if UNITY_2020_2_OR_NEWER +#if UNITY_2020_2_OR_NEWER || UNITY_DOTSRUNTIME var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), reflectionData, dependsOn, ScheduleMode.Parallel); #else var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), reflectionData, dependsOn, ScheduleMode.Batched); @@ -73,15 +72,12 @@ private static void CheckReflectionDataCorrect(IntPtr reflectionData) return JobsUtility.ScheduleParallelFor(ref scheduleParams, arrayLength, innerloopBatchCount); } - unsafe internal static void Run(this T jobData, int arrayLength) where T : struct, IJobParallelForBurstScheduable + unsafe internal static void Run(this T jobData, int arrayLength) where T : struct, IJobParallelForBurstSchedulable { - var reflectionData = ParallelForJobStructBurstScheduable.jobReflectionData.Data; + var reflectionData = ParallelForJobStructBurstSchedulable.jobReflectionData.Data; CheckReflectionDataCorrect(reflectionData); var scheduleParams = new JobsUtility.JobScheduleParameters(UnsafeUtility.AddressOf(ref jobData), reflectionData, new JobHandle(), ScheduleMode.Run); JobsUtility.ScheduleParallelFor(ref scheduleParams, arrayLength, arrayLength); } } -#else - internal interface IJobParallelForBurstScheduable : IJobParallelFor {} -#endif } diff --git a/Unity.Entities/IJobParallelForBurstScheduable.cs.meta b/Unity.Entities/IJobParallelForBurstSchedulable.cs.meta similarity index 100% rename from Unity.Entities/IJobParallelForBurstScheduable.cs.meta rename to Unity.Entities/IJobParallelForBurstSchedulable.cs.meta diff --git a/Unity.Entities/Iterators/ChunkDataGatherJobs.cs b/Unity.Entities/Iterators/ChunkDataGatherJobs.cs index 01d0b2b9..4d6b2fba 100644 --- a/Unity.Entities/Iterators/ChunkDataGatherJobs.cs +++ b/Unity.Entities/Iterators/ChunkDataGatherJobs.cs @@ -7,7 +7,7 @@ namespace Unity.Entities { [BurstCompile] - unsafe struct GatherChunks : IJobParallelForBurstScheduable + unsafe struct GatherChunks : IJobParallelForBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* entityComponentStore; [NativeDisableUnsafePtrRestriction] public MatchingArchetype** MatchingArchetypes; @@ -30,7 +30,7 @@ public void Execute(int index) } [BurstCompile] - internal unsafe struct GatherChunksAndOffsetsJob : IJobBurstScheduable + internal unsafe struct GatherChunksAndOffsetsJob : IJobBurstSchedulable { public UnsafeMatchingArchetypePtrList Archetypes; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* entityComponentStore; @@ -71,7 +71,7 @@ public void Execute() } [BurstCompile] - internal unsafe struct GatherChunksWithFiltering : IJobParallelForBurstScheduable + internal unsafe struct GatherChunksWithFiltering : IJobParallelForBurstSchedulable { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* entityComponentStore; [NativeDisableUnsafePtrRestriction] public MatchingArchetype** MatchingArchetypes; @@ -103,7 +103,7 @@ public void Execute(int index) } [BurstCompile] - internal unsafe struct GatherChunksAndOffsetsWithFilteringJob : IJobBurstScheduable + internal unsafe struct GatherChunksAndOffsetsWithFilteringJob : IJobBurstSchedulable { public UnsafeMatchingArchetypePtrList Archetypes; public EntityQueryFilter Filter; @@ -149,7 +149,7 @@ public void Execute() } } - struct JoinChunksJob : IJobParallelForBurstScheduable + struct JoinChunksJob : IJobParallelForBurstSchedulable { [DeallocateOnJobCompletion][ReadOnly] public NativeArray DestinationOffsets; [DeallocateOnJobCompletion][ReadOnly] public NativeArray SparseChunks; diff --git a/Unity.Entities/Iterators/ChunkIterationUtility.cs b/Unity.Entities/Iterators/ChunkIterationUtility.cs index a508fcba..38391603 100644 --- a/Unity.Entities/Iterators/ChunkIterationUtility.cs +++ b/Unity.Entities/Iterators/ChunkIterationUtility.cs @@ -12,6 +12,7 @@ namespace Unity.Entities /// Enables iteration over chunks belonging to a set of archetypes. /// [BurstCompile] + [BurstCompatible(RequiredUnityDefine = "UNITY_2020_2_OR_NEWER && !NET_DOTS")] [GenerateBurstMonoInterop("ChunkIterationUtility")] internal unsafe partial struct ChunkIterationUtility { @@ -96,6 +97,128 @@ public static NativeArray CreateArchetypeChunkArrayWithoutSync(U } } + [BurstMonoInteropMethod(MakePublic = false)] + private static void _GatherChunksImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypesList, + int* offsets, ArchetypeChunk* chunks) + { + MatchingArchetype** matchingArchetypes = matchingArchetypesList.Ptr; + EntityComponentStore* entityComponentStore = matchingArchetypesList.entityComponentStore; + + for (int index = 0; index < matchingArchetypesList.Length; ++index) + { + var archetype = matchingArchetypes[index]->Archetype; + var chunkCount = archetype->Chunks.Count; + var offset = offsets[index]; + for (int i = 0; i < chunkCount; i++) + { + var srcChunk = archetype->Chunks[i]; + chunks[offset + i] = new ArchetypeChunk(srcChunk, entityComponentStore); + } + } + } + + [BurstMonoInteropMethod(MakePublic = false)] + private static void _GatherChunksWithFilterImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypePtrList, + ref EntityQueryFilter filter, + int* offsets, + int* filteredCounts, + ArchetypeChunk* sparseChunks) + { + EntityComponentStore* entityComponentStore = matchingArchetypePtrList.entityComponentStore; + MatchingArchetype** matchingArchetypes = matchingArchetypePtrList.Ptr; + + for(int index = 0; index < matchingArchetypePtrList.Length; index++) + { + int filteredCount = 0; + var match = matchingArchetypes[index]; + var archetype = match->Archetype; + int chunkCount = archetype->Chunks.Count; + var writeIndex = offsets[index]; + var archetypeChunks = archetype->Chunks; + + for (var i = 0; i < chunkCount; ++i) + { + if (match->ChunkMatchesFilter(i, ref filter)) + sparseChunks[writeIndex + filteredCount++] = + new ArchetypeChunk(archetypeChunks[i], entityComponentStore); + } + + filteredCounts[index] = filteredCount; + } + } + + [BurstMonoInteropMethod(MakePublic = false)] + private static void _JoinChunksImmediate(int* DestinationOffsets, ArchetypeChunk* SparseChunks, + int* Offsets,ArchetypeChunk* JoinedChunks, int archetypeCount) + { + for(int index = 0; index < archetypeCount; index++) + { + int destOffset = DestinationOffsets[index]; + int count = DestinationOffsets[index + 1] - destOffset; + if (count != 0) + UnsafeUtility.MemCpy(JoinedChunks + destOffset,SparseChunks + Offsets[index],count * sizeof(ArchetypeChunk)); + } + } + + public static NativeArray CreateArchetypeChunkArrayWithoutSyncImmediate(UnsafeMatchingArchetypePtrList matchingArchetypes, + Allocator allocator, ref EntityQueryFilter filter) + { + var archetypeCount = matchingArchetypes.Length; + + var offsets = new NativeArray(archetypeCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var chunkCount = 0; + { + for (int i = 0; i < matchingArchetypes.Length; ++i) + { + var archetype = matchingArchetypes.Ptr[i]->Archetype; + offsets[i] = chunkCount; + chunkCount += archetype->Chunks.Count; + } + } + + if (!filter.RequiresMatchesFilter) + { + var chunks = new NativeArray(chunkCount, allocator, NativeArrayOptions.UninitializedMemory); + GatherChunksImmediate(matchingArchetypes,(int *)offsets.GetUnsafeReadOnlyPtr(),(ArchetypeChunk*)chunks.GetUnsafePtr()); + + offsets.Dispose(); + + return chunks; + } + else + { + var filteredCounts = new NativeArray(archetypeCount + 1, Allocator.TempJob); + var sparseChunks = new NativeArray(chunkCount, Allocator.TempJob, + NativeArrayOptions.UninitializedMemory); + + GatherChunksWithFilterImmediate(matchingArchetypes,ref filter,(int *)offsets.GetUnsafeReadOnlyPtr(), + (int *)filteredCounts.GetUnsafePtr(),(ArchetypeChunk*) sparseChunks.GetUnsafePtr()); + + + // accumulated filtered counts: filteredCounts[i] becomes the destination offset + int totalChunks = 0; + for (int i = 0; i < archetypeCount; ++i) + { + int currentCount = filteredCounts[i]; + filteredCounts[i] = totalChunks; + totalChunks += currentCount; + } + filteredCounts[archetypeCount] = totalChunks; + + var joinedChunks = new NativeArray(totalChunks, allocator, NativeArrayOptions.UninitializedMemory); + + JoinChunksImmediate((int*)filteredCounts.GetUnsafeReadOnlyPtr(), + (ArchetypeChunk*)sparseChunks.GetUnsafeReadOnlyPtr(), (int *) offsets.GetUnsafeReadOnlyPtr(), + (ArchetypeChunk *) joinedChunks.GetUnsafePtr(), archetypeCount); + + filteredCounts.Dispose(); + sparseChunks.Dispose(); + offsets.Dispose(); + + return joinedChunks; + } + } + /// /// Creates a NativeArray with all the chunks in a given archetype filtered by the provided EntityQueryFilter. /// This function will sync the needed types in the EntityQueryFilter. @@ -149,6 +272,7 @@ public static NativeArray CreateEntityArray(UnsafeMatchingArchetypePtrLi return job.Entities; } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER && !NET_DOTS")] public static NativeArray CreateComponentDataArray(UnsafeMatchingArchetypePtrList matchingArchetypes, Allocator allocator, ComponentTypeHandle typeHandle, @@ -175,17 +299,24 @@ public static NativeArray CreateComponentDataArray(UnsafeMatchingArchetype // Let's not forget that calls to ForEach can be re-entrant, so we need to cover this use case too. // The current solution is to allocate an array of a fixed size (16kb) where will will store the result, we will fall back to the jobified implementation if we run out of space in the buffer static readonly int k_EntityQueryResultBufferSize = 16384 / sizeof(Entity); - static Entity* s_EntityQueryResultBuffer = null; - internal static int currentOffsetInResultBuffer = 0; + struct ResultBufferTag { } + static readonly SharedStatic s_EntityQueryResultBuffer = SharedStatic.GetOrCreate(); + static readonly SharedStatic s_CurrentOffsetInResultBuffer = SharedStatic.GetOrCreate(); + + internal static int currentOffsetInResultBuffer + { + get { return s_CurrentOffsetInResultBuffer.Data; } + set { s_CurrentOffsetInResultBuffer.Data = value; } + } public static void GatherEntitiesToArray(EntityQueryData* queryData, ref EntityQueryFilter filter, out EntityQuery.GatherEntitiesResult result) { - if (s_EntityQueryResultBuffer == null) + if (s_EntityQueryResultBuffer.Data == IntPtr.Zero) { - s_EntityQueryResultBuffer = (Entity*)UnsafeUtility.Malloc(k_EntityQueryResultBufferSize * sizeof(Entity), 64, Allocator.Persistent); + s_EntityQueryResultBuffer.Data = (IntPtr)UnsafeUtility.Malloc(k_EntityQueryResultBufferSize * sizeof(Entity), 64, Allocator.Persistent); } - var buffer = s_EntityQueryResultBuffer; + var buffer = (Entity*) s_EntityQueryResultBuffer.Data; var curOffset = currentOffsetInResultBuffer; // Main method that copies the entities of each chunk of a matching archetype to the buffer @@ -240,12 +371,13 @@ bool AddArchetype(MatchingArchetype* matchingArchetype, ref EntityQueryFilter qu if (success) { result.EntityCount = curOffset - currentOffsetInResultBuffer; - result.EntityBuffer = s_EntityQueryResultBuffer + currentOffsetInResultBuffer; + result.EntityBuffer = (Entity*)s_EntityQueryResultBuffer.Data + currentOffsetInResultBuffer; } currentOffsetInResultBuffer = curOffset; } + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER && !NET_DOTS")] public static void CopyFromComponentDataArray(UnsafeMatchingArchetypePtrList matchingArchetypes, NativeArray componentDataArray, ComponentTypeHandle typeHandle, @@ -347,7 +479,26 @@ static int _CalculateChunkCount(ref UnsafeMatchingArchetypePtrList matchingArche return totalChunkCount; } + [BurstMonoInteropMethod(MakePublic = true)] + static void _RebuildChunkListCache(EntityQueryData* queryData) + { + ref var cache = ref queryData->MatchingChunkCache; + + cache.MatchingChunks.Clear(); + cache.PerChunkMatchingArchetypeIndex.Clear(); + + var archetypes = queryData->MatchingArchetypes; + for (int matchingArchetypeIndex = 0; matchingArchetypeIndex < archetypes.Length; ++matchingArchetypeIndex) + { + var archetype = archetypes.Ptr[matchingArchetypeIndex]->Archetype; + archetype->Chunks.AddToCachedChunkList(ref cache, matchingArchetypeIndex); + } + + cache.CacheValid = true; + } + #if ENABLE_UNITY_COLLECTIONS_CHECKS + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] internal static BufferAccessor GetChunkBufferAccessor(Chunk* chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion, AtomicSafetyHandle safety0, AtomicSafetyHandle safety1) #else internal static BufferAccessor GetChunkBufferAccessor(Chunk * chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion) @@ -442,21 +593,9 @@ internal static JobHandle PreparePrefilteredChunkLists(int unfilteredChunkCount, internal static void UnpackPrefilterData(NativeArray prefilterData, out ArchetypeChunk* chunks, out int* entityOffsets, out int filteredChunkCount) { -#if UNITY_DOTSRUNTIME - // TODO should be GetUnsafePtr(). Working around a bug in the (DOTS-Runtime) safety checks. - // https://unity3d.atlassian.net/browse/DOTSR-927 - chunks = (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(prefilterData); -#else chunks = (ArchetypeChunk*)prefilterData.GetUnsafePtr(); -#endif - -#if UNITY_DOTSRUNTIME - // TODO should be GetUnsafePtr(). Working around a bug in the (DOTS-Runtime) safety checks. - // https://unity3d.atlassian.net/browse/DOTSR-927 - filteredChunkCount = *(int*)((byte*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(prefilterData) + prefilterData.Length - sizeof(int)); -#else filteredChunkCount = *(int*)((byte*)prefilterData.GetUnsafePtr() + prefilterData.Length - sizeof(int)); -#endif + Assert.IsTrue(filteredChunkCount >= 0); entityOffsets = (int*)(chunks + filteredChunkCount); } diff --git a/Unity.Entities/Iterators/ChunkIterationUtility.interop.gen.cs b/Unity.Entities/Iterators/ChunkIterationUtility.interop.gen.cs index 53ba9262..95d5a538 100644 --- a/Unity.Entities/Iterators/ChunkIterationUtility.interop.gen.cs +++ b/Unity.Entities/Iterators/ChunkIterationUtility.interop.gen.cs @@ -14,6 +14,7 @@ using System; using Unity.Burst; +using Unity.Collections; using System.Runtime.InteropServices; namespace Unity.Entities @@ -38,25 +39,122 @@ private static bool UseDelegate() return result; } + private delegate void _dlg_GatherChunksImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypesList, int* offsets, ArchetypeChunk* chunks); + private static _dlg_GatherChunksImmediate _bfp_GatherChunksImmediate; + private delegate void _dlg_GatherChunksWithFilterImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypePtrList, ref EntityQueryFilter filter, int* offsets, int* filteredCounts, ArchetypeChunk* sparseChunks); + private static _dlg_GatherChunksWithFilterImmediate _bfp_GatherChunksWithFilterImmediate; + private delegate void _dlg_JoinChunksImmediate(int* DestinationOffsets, ArchetypeChunk* SparseChunks, int* Offsets, ArchetypeChunk* JoinedChunks, int archetypeCount); + private static _dlg_JoinChunksImmediate _bfp_JoinChunksImmediate; private delegate int _dlg_CalculateEntityCount(ref UnsafeMatchingArchetypePtrList matchingArchetypes, ref EntityQueryFilter filter); private static _dlg_CalculateEntityCount _bfp_CalculateEntityCount; private delegate int _dlg_CalculateChunkCount(ref UnsafeMatchingArchetypePtrList matchingArchetypes, ref EntityQueryFilter filter); private static _dlg_CalculateChunkCount _bfp_CalculateChunkCount; + private delegate void _dlg_RebuildChunkListCache(EntityQueryData* queryData); + private static _dlg_RebuildChunkListCache _bfp_RebuildChunkListCache; #endif + [NotBurstCompatible] internal static void Initialize() { #if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) if (_initialized) return; _initialized = true; + _bfp_GatherChunksImmediate = BurstCompiler.CompileFunctionPointer<_dlg_GatherChunksImmediate>(_mono_to_burst_GatherChunksImmediate).Invoke; + _bfp_GatherChunksWithFilterImmediate = BurstCompiler.CompileFunctionPointer<_dlg_GatherChunksWithFilterImmediate>(_mono_to_burst_GatherChunksWithFilterImmediate).Invoke; + _bfp_JoinChunksImmediate = BurstCompiler.CompileFunctionPointer<_dlg_JoinChunksImmediate>(_mono_to_burst_JoinChunksImmediate).Invoke; _bfp_CalculateEntityCount = BurstCompiler.CompileFunctionPointer<_dlg_CalculateEntityCount>(_mono_to_burst_CalculateEntityCount).Invoke; _bfp_CalculateChunkCount = BurstCompiler.CompileFunctionPointer<_dlg_CalculateChunkCount>(_mono_to_burst_CalculateChunkCount).Invoke; + _bfp_RebuildChunkListCache = BurstCompiler.CompileFunctionPointer<_dlg_RebuildChunkListCache>(_mono_to_burst_RebuildChunkListCache).Invoke; #endif } + private static void GatherChunksImmediate (in UnsafeMatchingArchetypePtrList matchingArchetypesList, int* offsets, ArchetypeChunk* chunks) + { +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + if (UseDelegate()) + { + _forward_mono_GatherChunksImmediate(in matchingArchetypesList, offsets, chunks); + return; + } +#endif + + _GatherChunksImmediate(in matchingArchetypesList, offsets, chunks); + } + +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + [BurstCompile] + [MonoPInvokeCallback(typeof(_dlg_GatherChunksImmediate))] + private static void _mono_to_burst_GatherChunksImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypesList, int* offsets, ArchetypeChunk* chunks) + { + _GatherChunksImmediate(in matchingArchetypesList, offsets, chunks); + } + + [BurstDiscard] + private static void _forward_mono_GatherChunksImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypesList, int* offsets, ArchetypeChunk* chunks) + { + _bfp_GatherChunksImmediate(in matchingArchetypesList, offsets, chunks); + } +#endif + + private static void GatherChunksWithFilterImmediate (in UnsafeMatchingArchetypePtrList matchingArchetypePtrList, ref EntityQueryFilter filter, int* offsets, int* filteredCounts, ArchetypeChunk* sparseChunks) + { +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + if (UseDelegate()) + { + _forward_mono_GatherChunksWithFilterImmediate(in matchingArchetypePtrList, ref filter, offsets, filteredCounts, sparseChunks); + return; + } +#endif + + _GatherChunksWithFilterImmediate(in matchingArchetypePtrList, ref filter, offsets, filteredCounts, sparseChunks); + } + +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + [BurstCompile] + [MonoPInvokeCallback(typeof(_dlg_GatherChunksWithFilterImmediate))] + private static void _mono_to_burst_GatherChunksWithFilterImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypePtrList, ref EntityQueryFilter filter, int* offsets, int* filteredCounts, ArchetypeChunk* sparseChunks) + { + _GatherChunksWithFilterImmediate(in matchingArchetypePtrList, ref filter, offsets, filteredCounts, sparseChunks); + } + + [BurstDiscard] + private static void _forward_mono_GatherChunksWithFilterImmediate(in UnsafeMatchingArchetypePtrList matchingArchetypePtrList, ref EntityQueryFilter filter, int* offsets, int* filteredCounts, ArchetypeChunk* sparseChunks) + { + _bfp_GatherChunksWithFilterImmediate(in matchingArchetypePtrList, ref filter, offsets, filteredCounts, sparseChunks); + } +#endif + + private static void JoinChunksImmediate (int* DestinationOffsets, ArchetypeChunk* SparseChunks, int* Offsets, ArchetypeChunk* JoinedChunks, int archetypeCount) + { +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + if (UseDelegate()) + { + _forward_mono_JoinChunksImmediate(DestinationOffsets, SparseChunks, Offsets, JoinedChunks, archetypeCount); + return; + } +#endif + + _JoinChunksImmediate(DestinationOffsets, SparseChunks, Offsets, JoinedChunks, archetypeCount); + } + +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + [BurstCompile] + [MonoPInvokeCallback(typeof(_dlg_JoinChunksImmediate))] + private static void _mono_to_burst_JoinChunksImmediate(int* DestinationOffsets, ArchetypeChunk* SparseChunks, int* Offsets, ArchetypeChunk* JoinedChunks, int archetypeCount) + { + _JoinChunksImmediate(DestinationOffsets, SparseChunks, Offsets, JoinedChunks, archetypeCount); + } + + [BurstDiscard] + private static void _forward_mono_JoinChunksImmediate(int* DestinationOffsets, ArchetypeChunk* SparseChunks, int* Offsets, ArchetypeChunk* JoinedChunks, int archetypeCount) + { + _bfp_JoinChunksImmediate(DestinationOffsets, SparseChunks, Offsets, JoinedChunks, archetypeCount); + } +#endif + public static int CalculateEntityCount (ref UnsafeMatchingArchetypePtrList matchingArchetypes, ref EntityQueryFilter filter) { #if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) @@ -115,6 +213,34 @@ private static void _forward_mono_CalculateChunkCount(ref int _retval, ref Unsaf } #endif + public static void RebuildChunkListCache (EntityQueryData* queryData) + { +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + if (UseDelegate()) + { + _forward_mono_RebuildChunkListCache(queryData); + return; + } +#endif + + _RebuildChunkListCache(queryData); + } + +#if !(UNITY_DOTSRUNTIME || (UNITY_2020_1_OR_NEWER && UNITY_IOS)) + [BurstCompile] + [MonoPInvokeCallback(typeof(_dlg_RebuildChunkListCache))] + private static void _mono_to_burst_RebuildChunkListCache(EntityQueryData* queryData) + { + _RebuildChunkListCache(queryData); + } + + [BurstDiscard] + private static void _forward_mono_RebuildChunkListCache(EntityQueryData* queryData) + { + _bfp_RebuildChunkListCache(queryData); + } +#endif + diff --git a/Unity.Entities/Iterators/DynamicBuffer.cs b/Unity.Entities/Iterators/DynamicBuffer.cs index e926af59..aa5dff1b 100644 --- a/Unity.Entities/Iterators/DynamicBuffer.cs +++ b/Unity.Entities/Iterators/DynamicBuffer.cs @@ -116,6 +116,12 @@ public int Capacity } } + /// + /// Reports whether container is empty. + /// + /// True if this container empty. + public bool IsEmpty => !IsCreated || Length == 0; + /// /// Whether the memory for this dynamic buffer has been allocated. /// diff --git a/Unity.Entities/Iterators/EntityQuery.cs b/Unity.Entities/Iterators/EntityQuery.cs index 156c3c5f..b1b744b0 100644 --- a/Unity.Entities/Iterators/EntityQuery.cs +++ b/Unity.Entities/Iterators/EntityQuery.cs @@ -426,6 +426,14 @@ public NativeArray CreateArchetypeChunkArray(Allocator allocator return res; } + internal NativeArray CreateArchetypeChunkArrayImmediate(Allocator allocator) + { + SyncFilterTypes(); + var res = ChunkIterationUtility.CreateArchetypeChunkArrayWithoutSyncImmediate(_QueryData->MatchingArchetypes, allocator, ref _Filter); + return res; + } + + public NativeArray ToEntityArrayAsync(Allocator allocator, out JobHandle jobhandle, EntityQuery outer) { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -897,6 +905,11 @@ public bool HasFilter() return _Filter.RequiresMatchesFilter; } + internal bool CheckChunkListCacheConsistency() + { + return UnsafeCachedChunkList.CheckCacheConsistency(ref _QueryData->MatchingChunkCache, _QueryData); + } + internal static EntityQueryImpl* Allocate() { void* ptr = UnsafeUtility.Malloc(sizeof(EntityQueryImpl), 8, Allocator.Persistent); @@ -1171,6 +1184,8 @@ internal IDisposable _CachedState /// Allocator to use for the array. /// NativeArray of all the chunks in this ComponentChunkIterator. public NativeArray CreateArchetypeChunkArray(Allocator allocator) => _GetImpl()->CreateArchetypeChunkArray(allocator); + + internal NativeArray CreateArchetypeChunkArrayImmediate(Allocator allocator) => _GetImpl()->CreateArchetypeChunkArrayImmediate(allocator); /// /// Creates a NativeArray containing the selected entities. /// @@ -1408,6 +1423,10 @@ public void AddSharedComponentFilter(SharedComponent sharedComp /// Returns true if the query has a filter, returns false if the query does not have a filter. public bool HasFilter() => _GetImpl()->HasFilter(); + internal void UpdateCache() => ChunkIterationUtility.RebuildChunkListCache(_GetImpl()->_QueryData); + internal bool CheckChunkListCacheConsistency() => _GetImpl()->CheckChunkListCacheConsistency(); + internal bool IsCacheValid => _GetImpl()->_QueryData->MatchingChunkCache.IsCacheValid; + /// /// Internal gen impl /// diff --git a/Unity.Entities/Iterators/EntityQueryManager.cs b/Unity.Entities/Iterators/EntityQueryManager.cs index 7ecf70ed..f228bea9 100644 --- a/Unity.Entities/Iterators/EntityQueryManager.cs +++ b/Unity.Entities/Iterators/EntityQueryManager.cs @@ -487,6 +487,7 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, var ecs = access->EntityComponentStore; cachedQuery->MatchingArchetypes = new UnsafeMatchingArchetypePtrList(access->EntityComponentStore); + cachedQuery->MatchingChunkCache = new UnsafeCachedChunkList(access->EntityComponentStore); cachedQuery->EntityQueryMask = new EntityQueryMask(); @@ -498,6 +499,7 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, groupCache.Add(hash, m_EntityGroupDatas.Length); m_EntityGroupDatas.Add(cachedQuery); + cachedQuery->MatchingChunkCache.InvalidateCache(); } return EntityQuery.Construct(cachedQuery, access); @@ -576,6 +578,9 @@ void AddArchetypeIfMatching(Archetype* archetype, EntityQueryData* query) query->MatchingArchetypes.Add(match); + // Add back pointer from archetype to query data + archetype->MatchingQueryData.Add(query); + for (var component = 0; component < query->RequiredComponentsCount; ++component) { var typeComponentIndex = -1; @@ -819,6 +824,76 @@ public override int GetHashCode() } } + unsafe struct UnsafeCachedChunkList + { + [NativeDisableUnsafePtrRestriction] + internal UnsafeChunkPtrList MatchingChunks; + + [NativeDisableUnsafePtrRestriction] + internal UnsafeIntList PerChunkMatchingArchetypeIndex; + + [NativeDisableUnsafePtrRestriction] + internal EntityComponentStore* EntityComponentStore; + + internal bool CacheValid; + + public Chunk** Ptr { get => (Chunk**)MatchingChunks.Ptr; } + public int Length { get => MatchingChunks.Length; } + public bool IsCacheValid { get => CacheValid; } + + public UnsafeCachedChunkList(EntityComponentStore* entityComponentStore) + { + EntityComponentStore = entityComponentStore; + MatchingChunks = new UnsafeChunkPtrList(0, Allocator.Persistent); + PerChunkMatchingArchetypeIndex = new UnsafeIntList(0, Allocator.Persistent); + CacheValid = false; + } + + public void Append(Chunk** t, int addChunkCount, int matchingArchetypeIndex) + { + var startIndex = MatchingChunks.Length; + MatchingChunks.AddRange(new UnsafeChunkPtrList(t, addChunkCount)); + for (int i = 0; i < addChunkCount; ++i) + { + PerChunkMatchingArchetypeIndex.Add(matchingArchetypeIndex); + } + } + + public void Dispose() + { + MatchingChunks.Dispose(); + PerChunkMatchingArchetypeIndex.Dispose(); + } + + public void InvalidateCache() + { + CacheValid = false; + } + + public static bool CheckCacheConsistency(ref UnsafeCachedChunkList cache, EntityQueryData* data) + { + var chunkCounter = 0; + for (int archetypeIndex = 0; archetypeIndex < data->MatchingArchetypes.Length; ++archetypeIndex) + { + var archetype = data->MatchingArchetypes.Ptr[archetypeIndex]->Archetype; + for (int chunkIndex = 0; chunkIndex < archetype->Chunks.Count; ++chunkIndex) + { + if (chunkCounter < cache.MatchingChunks.Length - 1) + return false; + + if(cache.MatchingChunks.Ptr[chunkCounter++] != archetype->Chunks[chunkIndex]) + return false; + } + } + + // All chunks in cache are accounted for + if (chunkCounter != cache.Length) + return false; + + return true; + } + } + unsafe struct EntityQueryData : IDisposable { //@TODO: better name or remove entirely... @@ -837,10 +912,20 @@ unsafe struct EntityQueryData : IDisposable public EntityQueryMask EntityQueryMask; public UnsafeMatchingArchetypePtrList MatchingArchetypes; + internal UnsafeCachedChunkList MatchingChunkCache; + + public unsafe UnsafeCachedChunkList GetMatchingChunkCache() + { + if(!MatchingChunkCache.IsCacheValid) + ChunkIterationUtility.RebuildChunkListCache((EntityQueryData*)UnsafeUtility.AddressOf(ref this)); + + return MatchingChunkCache; + } public void Dispose() { MatchingArchetypes.Dispose(); + MatchingChunkCache.Dispose(); } } } diff --git a/Unity.Entities/JobComponentSystem.cs b/Unity.Entities/JobComponentSystem.cs index fe0d87da..cd8cfa3a 100644 --- a/Unity.Entities/JobComponentSystem.cs +++ b/Unity.Entities/JobComponentSystem.cs @@ -123,27 +123,21 @@ public sealed override void Update() var inputJob = BeforeOnUpdate(); JobHandle outputJob = new JobHandle(); -#if ENABLE_UNITY_COLLECTIONS_CHECKS var oldExecutingSystem = ms_ExecutingSystem; ms_ExecutingSystem = this; -#endif try { outputJob = OnUpdate(inputJob); } catch { -#if ENABLE_UNITY_COLLECTIONS_CHECKS ms_ExecutingSystem = oldExecutingSystem; -#endif AfterOnUpdate(outputJob, false); throw; } -#if ENABLE_UNITY_COLLECTIONS_CHECKS ms_ExecutingSystem = oldExecutingSystem; -#endif AfterOnUpdate(outputJob, true); } diff --git a/Unity.Entities/ScriptBehaviourUpdateOrder.cs b/Unity.Entities/ScriptBehaviourUpdateOrder.cs index 047d766e..ea573a88 100644 --- a/Unity.Entities/ScriptBehaviourUpdateOrder.cs +++ b/Unity.Entities/ScriptBehaviourUpdateOrder.cs @@ -39,10 +39,20 @@ public UpdateAfterAttribute(Type systemType) public Type SystemType { get; } } - // The specified Type must be a ComponentSystemGroup. - // Updating in a group means this system will be automatically updated by the specified ComponentSystemGroup. - // The system may order itself relative to other systems in the group with UpdateBegin and UpdateEnd, - // There is nothing preventing systems from being in multiple groups, it can be added if there is a use-case for it + /// + /// The specified Type must be a ComponentSystemGroup. + /// Updating in a group means this system will be automatically updated by the specified ComponentSystemGroup when the group is updated. + /// The system may order itself relative to other systems in the group with UpdateBefore and UpdateAfter. This ordering takes + /// effect when the system group is sorted. + /// + /// If the optional OrderFirst parameter is set to true, this system will act as if it has an implicit [UpdateBefore] targeting all other + /// systems in the group that do *not* have OrderFirst=true, but it may still order itself relative to other systems with OrderFirst=true. + /// + /// If the optional OrderLast parameter is set to true, this system will act as if it has an implicit [UpdateAfter] targeting all other + /// systems in the group that do *not* have OrderLast=true, but it may still order itself relative to other systems with OrderLast=true. + /// + /// An UpdateInGroup attribute with both OrderFirst=true and OrderLast=true is invalid, and will throw an exception. + /// [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct)] public class UpdateInGroupAttribute : Attribute { @@ -98,7 +108,8 @@ static bool AppendSystemToPlayerLoopListImpl(ComponentSystemBase system, ref Pla /// /// The ECS system to add to the player loop. /// Existing player loop to modify (e.g. PlayerLoop.GetCurrentPlayerLoop()) - /// The Type of the PlayerLoopSystem subsystem to which the ECS system should be appended. + /// The Type of the PlayerLoopSystem subsystem to which the ECS system should be appended. + /// See the UnityEngine.PlayerLoop namespace for valid values. public static void AppendSystemToPlayerLoopList(ComponentSystemBase system, ref PlayerLoopSystem playerLoop, Type playerLoopSystemType) { if (!AppendSystemToPlayerLoopListImpl(system, ref playerLoop, playerLoopSystemType)) diff --git a/Unity.Entities/SystemBase.cs b/Unity.Entities/SystemBase.cs index 37c16a74..f09ecdd0 100644 --- a/Unity.Entities/SystemBase.cs +++ b/Unity.Entities/SystemBase.cs @@ -94,6 +94,7 @@ namespace Unity.Entities /// [Entities.ForEach]: xref:ecs-entities-foreach /// [Job.WithCode]: xref:ecs-entities-foreach /// + [BurstCompile] public unsafe abstract class SystemBase : ComponentSystemBase { /// @@ -389,19 +390,15 @@ public sealed override void Update() state->BeforeOnUpdate(); -#if ENABLE_UNITY_COLLECTIONS_CHECKS var oldExecutingSystem = ms_ExecutingSystem; ms_ExecutingSystem = this; -#endif try { OnUpdate(); } catch { -#if ENABLE_UNITY_COLLECTIONS_CHECKS ms_ExecutingSystem = oldExecutingSystem; -#endif state->AfterOnUpdate(); @@ -414,9 +411,7 @@ public sealed override void Update() throw; } -#if ENABLE_UNITY_COLLECTIONS_CHECKS ms_ExecutingSystem = oldExecutingSystem; -#endif state->AfterOnUpdate(); @@ -440,6 +435,8 @@ public sealed override void Update() } } + [BurstCompile] + [MonoPInvokeCallback(typeof(Func))] internal static bool UnmanagedUpdate(SystemState* state, out SystemDependencySafetyUtility.SafetyErrorDetails errorDetails) { var hasSafetyError = false; diff --git a/Unity.Entities/SystemBaseRegistry.cs b/Unity.Entities/SystemBaseRegistry.cs index 10d2d1a2..c5020274 100644 --- a/Unity.Entities/SystemBaseRegistry.cs +++ b/Unity.Entities/SystemBaseRegistry.cs @@ -302,7 +302,7 @@ internal static void CheckBurst(ref bool status) [BurstCompatible] internal static unsafe void CallForwardingFunction(SystemState* systemState, int index) { - var metaIndex = systemState->m_UnmanagedMetaIndex; + var metaIndex = systemState->UnmanagedMetaIndex; var systemPointer = systemState->m_SystemPtr; var delegates = s_Data.Data.GetSystemDelegates(metaIndex); bool isBurst = true; diff --git a/Unity.Entities/SystemState.cs b/Unity.Entities/SystemState.cs new file mode 100644 index 00000000..53832ab8 --- /dev/null +++ b/Unity.Entities/SystemState.cs @@ -0,0 +1,648 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Core; +using Unity.Jobs; +using Unity.Jobs.LowLevel.Unsafe; +using Unity.Profiling; + +namespace Unity.Entities +{ + /// + /// Contains raw entity system state. Used by unmanaged systems (ISystemBase) as well as managed systems behind the scenes. + /// + [BurstCompatible(RequiredUnityDefine = "UNITY_2020_2_OR_NEWER && !UNITY_DOTSRUNTIME")] + public unsafe struct SystemState + { + // For unmanaged systems, points to user struct that was allocated to front this system state + internal void* m_SystemPtr; + + private UnsafeList m_EntityQueries; + private UnsafeList m_RequiredEntityQueries; + + /// + /// Return a debug name for unmanaged systems. + /// + public FixedString64 DebugName => UnmanagedMetaIndex >= 0 ? SystemBaseRegistry.GetDebugName(UnmanagedMetaIndex) : default; + + internal ref UnsafeList EntityQueries + { + get + { + fixed(void* ptr = &m_EntityQueries) + { + return ref UnsafeUtility.AsRef>(ptr); + } + } + } + + internal ref UnsafeList RequiredEntityQueries + { + get + { + fixed(void* ptr = &m_RequiredEntityQueries) + { + return ref UnsafeUtility.AsRef>(ptr); + } + } + } + + internal UnsafeIntList m_JobDependencyForReadingSystems; + internal UnsafeIntList m_JobDependencyForWritingSystems; + + internal uint m_LastSystemVersion; + + internal EntityManager m_EntityManager; + internal EntityComponentStore* m_EntityComponentStore; + internal ComponentDependencyManager* m_DependencyManager; + internal GCHandle m_World; + internal WorldUnmanaged m_WorldUnmanaged; + + internal bool m_AlwaysUpdateSystem; + internal bool m_Enabled; + internal bool m_PreviouslyEnabled; + + // SystemBase stuff + internal bool m_GetDependencyFromSafetyManager; + internal JobHandle m_JobHandle; + + #if ENABLE_PROFILER + internal ProfilerMarker m_ProfilerMarker; + #endif + + internal int m_SystemID; + + /// + /// Controls whether this system executes when its OnUpdate function is called. + /// + /// True, if the system is enabled. + /// The Enabled property is intended for debugging so that you can easily turn on and off systems + /// from the Entity Debugger window. A system with Enabled set to false will not update, even if its + /// function returns true. + public bool Enabled { get => m_Enabled; set => m_Enabled = value; } + + /// + /// The current change version number in this . + /// + /// The system updates the component version numbers inside any instances + /// that this system accesses with write permissions to this value. + public uint GlobalSystemVersion => m_EntityComponentStore->GlobalSystemVersion; + + /// + /// The current version of this system. + /// + /// + /// LastSystemVersion is updated to match the whenever a system runs. + /// + /// When you use + /// or , LastSystemVersion provides the basis for determining + /// whether a component could have changed since the last time the system ran. + /// + /// When a system accesses a component and has write permission, it updates the change version of that component + /// type to the current value of LastSystemVersion. The system updates the component type's version whether or not + /// it actually modifies data in any instances of the component type -- this is one reason why you should + /// specify read-only access to components whenever possible. + /// + /// For efficiency, ECS tracks the change version of component types by chunks, not by individual entities. If a system + /// updates the component of a given type for any entity in a chunk, then ECS assumes that the components of all + /// entities in that chunk could have been changed. Change filtering allows you to save processing time by + /// skipping all entities in an unchanged chunk, but does not support skipping individual entities in a chunk + /// that does contain changes. + /// + /// The the last time this system ran. + public uint LastSystemVersion => m_LastSystemVersion; + + /// + /// The EntityManager object of the in which this system exists. + /// + /// The EntityManager for this system. + public EntityManager EntityManager => m_EntityManager; + + /// + /// The World in which this system exists. + /// + /// The World of this system. + [NotBurstCompatible] + public World World => (World)m_World.Target; + + /// + /// The unmanaged portion of the world in which this system exists. + /// + /// The unmanaged world of this system. + public WorldUnmanaged WorldUnmanaged => m_WorldUnmanaged; + + /// + /// The current Time data for this system's world. + /// + public ref readonly TimeData Time => ref WorldUnmanaged.CurrentTime; + + // Used by managed systems to heap allocate a system state when they are created. + // Unmanaged systems use a separate allocator optimized for speed, found in World.cs + internal static SystemState* Allocate() + { + void* result = UnsafeUtility.Malloc(sizeof(SystemState), 16, Allocator.Persistent); + UnsafeUtility.MemClear(result, sizeof(SystemState)); + return (SystemState*)result; + } + + internal static void Deallocate(SystemState* state) + { + UnsafeUtility.Free(state, Allocator.Persistent); + } + + // Managed systems call this function to initialize their backing system state + [NotBurstCompatible] // Because world + internal void InitManaged(World world, Type managedType) + { + UnmanagedMetaIndex = -1; + + CommonInit(world); + + if (managedType != null) + { +#if !UNITY_DOTSRUNTIME + m_AlwaysUpdateSystem = Attribute.IsDefined(managedType, typeof(AlwaysUpdateSystemAttribute), true); +#else + var attrs = TypeManager.GetSystemAttributes(managedType, typeof(AlwaysUpdateSystemAttribute)); + if (attrs.Length > 0) + m_AlwaysUpdateSystem = true; +#endif + } + +#if ENABLE_PROFILER + m_ProfilerMarker = new Profiling.ProfilerMarker($"{world.Name} {TypeManager.GetSystemName(managedType)}"); +#endif + } + + // Initialization common to managed and unmanaged systems + private void CommonInit(World world) + { + m_Enabled = true; + m_SystemID = World.AllocateSystemID(); + m_World = GCHandle.Alloc(world); + m_WorldUnmanaged = world.Unmanaged; + m_EntityManager = world.EntityManager; + m_EntityComponentStore = m_EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + m_DependencyManager = m_EntityManager.GetCheckedEntityDataAccess()->DependencyManager; + + EntityQueries = new UnsafeList(0, Allocator.Persistent); + RequiredEntityQueries = new UnsafeList(0, Allocator.Persistent); + + m_JobDependencyForReadingSystems = new UnsafeIntList(0, Allocator.Persistent); + m_JobDependencyForWritingSystems = new UnsafeIntList(0, Allocator.Persistent); + + m_AlwaysUpdateSystem = false; + } + + // Unmanaged systems call this function to initialize their backing system state + [NotBurstCompatible] + internal void InitUnmanaged(World world, int unmanagedMetaIndex, void* systemptr) + { + m_Enabled = true; + UnmanagedMetaIndex = unmanagedMetaIndex; + m_SystemPtr = systemptr; + + CommonInit(world); + +#if ENABLE_PROFILER + m_ProfilerMarker = new Profiling.ProfilerMarker($"{world.Name} {SystemBaseRegistry.GetDebugName(UnmanagedMetaIndex)}"); +#endif + + // TODO: AlwaysUpdate reflection code needs to go here as for managed, or be backed into systembaseregistry and/or typemanager + } + + [NotBurstCompatible] + internal void Dispose() + { + DisposeQueries(ref EntityQueries); + DisposeQueries(ref RequiredEntityQueries); + + EntityQueries.Dispose(); + EntityQueries = default; + + RequiredEntityQueries.Dispose(); + RequiredEntityQueries = default; + + if (m_World.IsAllocated) + { + m_World.Free(); + } + + m_JobDependencyForReadingSystems.Dispose(); + m_JobDependencyForWritingSystems.Dispose(); + } + + private void DisposeQueries(ref UnsafeList queries) + { + for (var i = 0; i < queries.Length; ++i) + { + var query = queries[i]; + + if (m_EntityManager.IsQueryValid(query)) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + query._GetImpl()->_DisallowDisposing = false; +#endif + query.Dispose(); + } + } + } + + /// + /// The ECS-related data dependencies of the system. + /// + /// + /// Before , the Dependency property represents the combined job handles of any job that + /// writes to the same components that the current system reads -- or reads the same components that the current + /// system writes to. When you use [Entities.ForEach] or [Job.WithCode], the system uses the Dependency property + /// to specify a job’s dependencies when scheduling it. The system also combines the new job's [JobHandle] + /// with Dependency so that any subsequent job scheduled in the system depends on the earlier jobs (in sequence). + /// + /// You can opt out of this default dependency management by explicitly passing a [JobHandle] to + /// [Entities.ForEach] or [Job.WithCode]. When you pass in a [JobHandle], these constructions also return a + /// [JobHandle] representing the input dependencies combined with the new job. The [JobHandle] objects of any + /// jobs scheduled with explicit dependencies are not combined with the system’s Dependency property. You must set the Dependency + /// property manually to make sure that later systems receive the correct job dependencies. + /// + /// You can combine implicit and explicit dependency management (by using [JobHandle.CombineDependencies]); + /// however, doing so can be error prone. When you set the Dependency property, the assigned [JobHandle] + /// replaces any existing dependency, it is not combined with them. + /// + /// Note that the default, implicit dependency management does not include jobs. + /// You must manage the dependencies for IJobChunk explicitly. + /// + /// [JobHandle]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html + /// [JobHandle.CombineDependencies]: https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.CombineDependencies.html + /// [Entities.ForEach]: xref:ecs-entities-foreach + /// [Job.WithCode]: xref:ecs-entities-foreach + /// + public JobHandle Dependency + { + get + { + if (m_GetDependencyFromSafetyManager) + { + var depMgr = m_DependencyManager; + m_GetDependencyFromSafetyManager = false; + m_JobHandle = depMgr->GetDependency(m_JobDependencyForReadingSystems.Ptr, + m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, + m_JobDependencyForWritingSystems.Length); + } + + return m_JobHandle; + } + set + { + m_GetDependencyFromSafetyManager = false; + m_JobHandle = value; + } + } + + /// + /// Return the unmanaged type index of the system (>= 0 for ISystemBase-type systems), or -1 for managed systems. + /// + public int UnmanagedMetaIndex { get; private set; } + + public void CompleteDependency() + { + // Previous frame job + m_JobHandle.Complete(); + + // We need to get more job handles from other systems + if (m_GetDependencyFromSafetyManager) + { + m_GetDependencyFromSafetyManager = false; + CompleteDependencyInternal(); + } + } + + internal void CompleteDependencyInternal() + { + m_DependencyManager->CompleteDependenciesNoChecks(m_JobDependencyForReadingSystems.Ptr, + m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, + m_JobDependencyForWritingSystems.Length); + } + + internal void BeforeUpdateVersioning() + { + m_EntityComponentStore->IncrementGlobalSystemVersion(); + ref var qs = ref EntityQueries; + for (int i = 0; i < qs.Length; ++i) + { + qs[i].SetChangedFilterRequiredVersion(m_LastSystemVersion); + } + } + + internal void BeforeOnUpdate() + { + BeforeUpdateVersioning(); + + // We need to wait on all previous frame dependencies, otherwise it is possible that we create infinitely long dependency chains + // without anyone ever waiting on it + m_JobHandle.Complete(); + m_GetDependencyFromSafetyManager = true; + } + +#pragma warning disable 649 + private unsafe struct JobHandleData + { + public void* jobGroup; + public int version; + } +#pragma warning restore 649 + + internal void AfterOnUpdate() + { + AfterUpdateVersioning(); + + var depMgr = m_DependencyManager; + + // If outputJob says no relevant jobs were scheduled, + // then no need to batch them up or register them. + // This is a big optimization if we only Run methods on main thread... + var outputJob = m_JobHandle; + if (((JobHandleData*)&outputJob)->jobGroup != null) + { + JobHandle.ScheduleBatchedJobs(); + m_JobHandle = depMgr->AddDependency(m_JobDependencyForReadingSystems.Ptr, + m_JobDependencyForReadingSystems.Length, m_JobDependencyForWritingSystems.Ptr, + m_JobDependencyForWritingSystems.Length, outputJob); + } + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] + internal void CheckSafety(ref SystemDependencySafetyUtility.SafetyErrorDetails details, ref bool errorFound) + { + var depMgr = m_DependencyManager; + if (JobsUtility.JobDebuggerEnabled) + { + var dependencyError = SystemDependencySafetyUtility.CheckSafetyAfterUpdate(ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems, depMgr, out details); + + if (dependencyError) + { + SystemDependencySafetyUtility.EmergencySyncAllJobs(ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems, depMgr); + } + + errorFound = dependencyError; + } + } + +#endif + + internal void AfterUpdateVersioning() + { + m_LastSystemVersion = m_EntityComponentStore->GlobalSystemVersion; + } + + internal bool ShouldRunSystem() + { + if (m_AlwaysUpdateSystem) + return true; + + ref var required = ref RequiredEntityQueries; + + if (required.Length > 0) + { + for (int i = 0; i != required.Length; i++) + { + EntityQuery query = required[i]; + if (query.IsEmptyIgnoreFilter) + return false; + } + + return true; + } + else + { + // Systems without queriesDesc should always run. Specifically, + // IJobForEach adds its queriesDesc the first time it's run. + ref var eqs = ref EntityQueries; + var length = eqs.Length; + if (length == 0) + return true; + + // If all the queriesDesc are empty, skip it. + // (There’s no way to know what the key value is without other markup) + for (int i = 0; i != length; i++) + { + EntityQuery query = eqs[i]; + if (!query.IsEmptyIgnoreFilter) + return true; + } + + return false; + } + } + + [NotBurstCompatible] // We need to fix up EntityQueryManager + internal EntityQuery GetEntityQueryInternal(ComponentType* componentTypes, int count) + { + ref var handles = ref EntityQueries; + + for (var i = 0; i != handles.Length; i++) + { + var query = handles[i]; + + if (query.CompareComponents(componentTypes, count)) + return query; + } + + var newQuery = EntityManager.CreateEntityQuery(componentTypes, count); + + AddReaderWriters(newQuery); + AfterQueryCreated(newQuery); + + return newQuery; + } + + internal void AddReaderWriter(ComponentType componentType) + { + if (CalculateReaderWriterDependency.Add(componentType, ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems)) + { + CompleteDependencyInternal(); + } + } + + internal void AddReaderWriters(EntityQuery query) + { + if (query.AddReaderWritersToLists(ref m_JobDependencyForReadingSystems, ref m_JobDependencyForWritingSystems)) + { + CompleteDependencyInternal(); + } + } + + private void AfterQueryCreated(EntityQuery query) + { + query.SetChangedFilterRequiredVersion(m_LastSystemVersion); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + query._GetImpl()->_DisallowDisposing = true; +#endif + + EntityQueries.Add(query); + } + + [NotBurstCompatible] // We need to fix up EntityQueryManager + internal EntityQuery GetSingletonEntityQueryInternal(ComponentType type) + { + ref var handles = ref EntityQueries; + + for (var i = 0; i != handles.Length; i++) + { + var query = handles[i]; + var queryData = query._GetImpl()->_QueryData; + + // EntityQueries are constructed including the Entity ID + if (2 != queryData->RequiredComponentsCount) + continue; + + if (queryData->RequiredComponents[1] != type) + continue; + + return query; + } + + var newQuery = EntityManager.CreateEntityQuery(&type, 1); + + AddReaderWriters(newQuery); + AfterQueryCreated(newQuery); + + return newQuery; + } + + [NotBurstCompatible] + internal EntityQuery GetEntityQueryInternal(EntityQueryDesc[] desc) + { + ref var handles = ref EntityQueries; + + for (var i = 0; i != handles.Length; i++) + { + var query = handles[i]; + + if (query.CompareQuery(desc)) + return query; + } + + var newQuery = EntityManager.CreateEntityQuery(desc); + + AddReaderWriters(newQuery); + AfterQueryCreated(newQuery); + + return newQuery; + } + + /// + /// Gets the cached query for the specified component types, if one exists; otherwise, creates a new query + /// instance and caches it. + /// + /// An array or comma-separated list of component types. + /// The new or cached query. + [NotBurstCompatible] + public EntityQuery GetEntityQuery(params ComponentType[] componentTypes) + { + fixed (ComponentType* types = componentTypes) + { + return GetEntityQueryInternal(types, componentTypes.Length); + } + } + + /// + /// Gets the cached query for the specified component types, if one exists; otherwise, creates a new query + /// instance and caches it. + /// + /// An array of component types. + /// The new or cached query. + [NotBurstCompatible] + public EntityQuery GetEntityQuery(NativeArray componentTypes) + { + return GetEntityQueryInternal((ComponentType*)componentTypes.GetUnsafeReadOnlyPtr(), componentTypes.Length); + } + + /// + /// Combines an array of query description objects into a single query. + /// + /// This function looks for a cached query matching the combined query descriptions, and returns it + /// if one exists; otherwise, the function creates a new query instance and caches it. + /// The new or cached query. + /// An array of query description objects to be combined to define the query. + [NotBurstCompatible] + public EntityQuery GetEntityQuery(params EntityQueryDesc[] queryDesc) + { + return GetEntityQuery(queryDesc); + } + + /// + /// Gets the run-time type information required to access an array of component data in a chunk. + /// + /// Whether the component data is only read, not written. Access components as + /// read-only whenever possible. + /// A struct that implements . + /// An object representing the type information required to safely access component data stored in a + /// chunk. + /// Pass an instance to a job that has access to chunk data, + /// such as an job, to access that type of component inside the job. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] + public ComponentTypeHandle GetComponentTypeHandle(bool isReadOnly = false) where T : struct, IComponentData + { + AddReaderWriter(isReadOnly ? ComponentType.ReadOnly() : ComponentType.ReadWrite()); + return EntityManager.GetComponentTypeHandle(isReadOnly); + } + + /// + /// Gets the run-time type information required to access an array of component data in a chunk. + /// + /// Type of the component + /// An object representing the type information required to safely access component data stored in a + /// chunk. + /// Pass an DynamicComponentTypeHandle instance to a job that has access to chunk data, such as an + /// job, to access that type of component inside the job. + public DynamicComponentTypeHandle GetDynamicComponentTypeHandle(ComponentType componentType) + { + AddReaderWriter(componentType); + return EntityManager.GetDynamicComponentTypeHandle(componentType); + } + + /// + /// Gets the run-time type information required to access an array of buffer components in a chunk. + /// + /// Whether the data is only read, not written. Access data as + /// read-only whenever possible. + /// A struct that implements . + /// An object representing the type information required to safely access buffer components stored in a + /// chunk. + /// Pass a BufferTypeHandle instance to a job that has access to chunk data, such as an + /// job, to access that type of buffer component inside the job. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] + public BufferTypeHandle GetBufferTypeHandle(bool isReadOnly = false) + where T : struct, IBufferElementData + { + AddReaderWriter(isReadOnly ? ComponentType.ReadOnly() : ComponentType.ReadWrite()); + return EntityManager.GetBufferTypeHandle(isReadOnly); + } + + /// + /// Gets the run-time type information required to access a shared component data in a chunk. + /// + /// A struct that implements . + /// An object representing the type information required to safely access shared component data stored in a + /// chunk. + [BurstCompatible(GenericTypeArguments = new[] { typeof(BurstCompatibleSharedComponentData) })] + public SharedComponentTypeHandle GetSharedComponentTypeHandle() + where T : struct, ISharedComponentData + { + return EntityManager.GetSharedComponentTypeHandle(); + } + + /// + /// Gets the run-time type information required to access the array of objects in a chunk. + /// + /// An object representing the type information required to safely access Entity instances stored in a + /// chunk. + public EntityTypeHandle GetEntityTypeHandle() + { + return EntityManager.GetEntityTypeHandle(); + } + } +} diff --git a/Unity.Entities/SystemState.cs.meta b/Unity.Entities/SystemState.cs.meta new file mode 100644 index 00000000..5675d29f --- /dev/null +++ b/Unity.Entities/SystemState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f4fcf005bbdcdf445ad0345873d63bd0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities/Types/Archetype.cs b/Unity.Entities/Types/Archetype.cs index 2005b090..cba9ff29 100644 --- a/Unity.Entities/Types/Archetype.cs +++ b/Unity.Entities/Types/Archetype.cs @@ -28,6 +28,12 @@ internal unsafe struct Archetype public ChunkListMap FreeChunksBySharedComponents; public ComponentTypeInArchetype* Types; + // back pointer to EntityQueryData(s), used for chunk list caching + public UnsafePtrList MatchingQueryData; + + // single-linked list used for invalidating chunk list caches + public Archetype* NextChangedArchetype; + public int EntityCount; public int ChunkCapacity; @@ -114,7 +120,7 @@ public override string ToString() return info; } - public void AddToChunkList(Chunk *chunk, SharedComponentValues sharedComponentIndices, uint changeVersion) + public void AddToChunkList(Chunk *chunk, SharedComponentValues sharedComponentIndices, uint changeVersion, ref EntityComponentStore.ChunkListChanges changes) { chunk->ListIndex = Chunks.Count; if (Chunks.Count == Chunks.Capacity) @@ -135,13 +141,23 @@ public void AddToChunkList(Chunk *chunk, SharedComponentValues sharedComponentIn } Chunks.Add(chunk, sharedComponentIndices, changeVersion); + + fixed(Archetype* archetype = &this) + { + changes.TrackArchetype(archetype); + } } - public void RemoveFromChunkList(Chunk* chunk) + public void RemoveFromChunkList(Chunk* chunk, ref EntityComponentStore.ChunkListChanges changes) { Chunks.RemoveAtSwapBack(chunk->ListIndex); var chunkThatMoved = Chunks[chunk->ListIndex]; chunkThatMoved->ListIndex = chunk->ListIndex; + + fixed(Archetype* archetype = &this) + { + changes.TrackArchetype(archetype); + } } public void AddToChunkListWithEmptySlots(Chunk *chunk) diff --git a/Unity.Entities/Types/ArchetypeChunkData.cs b/Unity.Entities/Types/ArchetypeChunkData.cs index 54923119..366b2bdf 100644 --- a/Unity.Entities/Types/ArchetypeChunkData.cs +++ b/Unity.Entities/Types/ArchetypeChunkData.cs @@ -202,6 +202,11 @@ public void MoveChunks(in ArchetypeChunkData srcChunks) Count += srcChunks.Count; } + public void AddToCachedChunkList(ref UnsafeCachedChunkList chunkList, int matchingArchetypeIndex, int startIndex = 0) + { + chunkList.Append(p + startIndex, Count - startIndex, matchingArchetypeIndex); + } + public void RemoveAtSwapBack(int chunkIndex) { Count--; diff --git a/Unity.Entities/Types/BurstCompatibleComponents.cs b/Unity.Entities/Types/BurstCompatibleComponents.cs new file mode 100644 index 00000000..9f6eaf9e --- /dev/null +++ b/Unity.Entities/Types/BurstCompatibleComponents.cs @@ -0,0 +1,20 @@ +namespace Unity.Entities +{ + // This exists only to be able to make generic instances of generic methods to test burst compatiblity. + internal struct BurstCompatibleComponentData : IComponentData + { + public int UnusedField; + } + + // This exists only to be able to make generic instances of generic methods to test burst compatiblity. + internal struct BurstCompatibleSharedComponentData : ISharedComponentData + { + public int UnusedField; + } + + // This exists only to be able to make generic instances of generic methods to test burst compatiblity. + internal struct BurstCompatibleBufferElement : IBufferElementData + { + public int UnusedField; + } +} diff --git a/Unity.Entities/Types/BurstCompatibleComponents.cs.meta b/Unity.Entities/Types/BurstCompatibleComponents.cs.meta new file mode 100644 index 00000000..ea216448 --- /dev/null +++ b/Unity.Entities/Types/BurstCompatibleComponents.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb0b948154160cf478f1f3060051ace1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities/Types/ComponentType.cs b/Unity.Entities/Types/ComponentType.cs index e713e36d..ba12c606 100644 --- a/Unity.Entities/Types/ComponentType.cs +++ b/Unity.Entities/Types/ComponentType.cs @@ -185,14 +185,14 @@ public override string ToString() var info = TypeManager.GetTypeInfo(TypeIndex); FixedString512 ns = default; - ns.AppendFrom(new FixedString128(info.Debug.TypeName)); + ns.Append(info.Debug.TypeName); if (IsBuffer) - ns.AppendFrom(new FixedString32(" [B]")); + ns.Append(" [B]"); if (AccessModeType == AccessMode.Exclude) - ns.AppendFrom(new FixedString32(" [S]")); + ns.Append(" [S]"); if (AccessModeType == AccessMode.ReadOnly) - ns.AppendFrom(new FixedString32(" [RO]")); + ns.Append(" [RO]"); return ns.ToString(); #else diff --git a/Unity.Entities/Types/EarlyInitHelpers.cs b/Unity.Entities/Types/EarlyInitHelpers.cs new file mode 100644 index 00000000..21639482 --- /dev/null +++ b/Unity.Entities/Types/EarlyInitHelpers.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + +namespace Unity.Entities +{ + /// + /// Used by automatically generated code. Do not use in projects. + /// + public class EarlyInitHelpers + { + public delegate void EarlyInitFunction(); + + private static List s_PendingDelegates; + + public static void FlushEarlyInits() + { + while (s_PendingDelegates != null) + { + var oldList = s_PendingDelegates; + s_PendingDelegates = null; + + for (int i = 0; i < oldList.Count; ++i) + { + try + { + oldList[i](); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + } + } + + public static void AddEarlyInitFunction(EarlyInitFunction f) + { + if (s_PendingDelegates == null) + s_PendingDelegates = new List(); + + s_PendingDelegates.Add(f); + } + + public static void JobReflectionDataCreationFailed(Exception ex, Type jobType) + { + Debug.LogError($"Failed to create job reflection data for type ${jobType}:"); + Debug.LogException(ex); + } + } + +} diff --git a/Unity.Entities/Types/EarlyInitHelpers.cs.meta b/Unity.Entities/Types/EarlyInitHelpers.cs.meta new file mode 100644 index 00000000..0a0a0715 --- /dev/null +++ b/Unity.Entities/Types/EarlyInitHelpers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05a99ad2800ad2f49b22ab35d999bfc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities/Types/EntityArchetype.cs b/Unity.Entities/Types/EntityArchetype.cs index 3c1a358b..d22ee9ae 100644 --- a/Unity.Entities/Types/EntityArchetype.cs +++ b/Unity.Entities/Types/EntityArchetype.cs @@ -25,6 +25,8 @@ namespace Unity.Entities /// stores component data in blocks of memory called *chunks*. A given chunk stores only entities having the same /// archetype. You can get the EntityArchetype object for a chunk from its /// property. + /// + /// Instead of using new EntityArchetype(), use EntityManager.CreateArchetype() to create EntityArchetype values. /// [DebuggerTypeProxy(typeof(EntityArchetypeDebugView))] public unsafe struct EntityArchetype : IEquatable @@ -139,5 +141,14 @@ public NativeArray GetComponentTypes(Allocator allocator = Alloca /// /// True, if the archetype is a disabled archetype. public bool Disabled => Archetype->Disabled; + + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] + internal void CheckValidEntityArchetype() + { + if (!Valid) + { + throw new ArgumentException("EntityArchetype argument is invalid. Calling new EntityArchetype() produces an invalid value. Call EntityManager.CreateArchetype() to get a valid EntityArchetype value."); + } + } } } diff --git a/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs b/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs new file mode 100644 index 00000000..c74d8d38 --- /dev/null +++ b/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs @@ -0,0 +1,22 @@ +using System; + +namespace Unity.Entities +{ + /// + /// When added as an assembly-level attribute, allows creating job reflection data for instances of generic jobs. + /// + /// + /// This attribute allows specific instances of generic jobs to be registered for reflection data generation. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class RegisterGenericJobTypeAttribute: Attribute + { + public Type ConcreteType; + + public RegisterGenericJobTypeAttribute(Type type) + { + ConcreteType = type; + } + } + +} diff --git a/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs.meta b/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs.meta new file mode 100644 index 00000000..cb336216 --- /dev/null +++ b/Unity.Entities/Types/RegisterGenericJobTypeAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87962ebd9ebd46843a6db7a6fabfd0a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Entities/UnsafeList.gen.cs b/Unity.Entities/UnsafeList.gen.cs index 2dc61e35..58c471b5 100644 --- a/Unity.Entities/UnsafeList.gen.cs +++ b/Unity.Entities/UnsafeList.gen.cs @@ -19,7 +19,9 @@ namespace Unity.Entities { [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(UnsafeIntListDebugView))] - internal unsafe struct UnsafeIntList : IDisposable + internal unsafe struct UnsafeIntList + : INativeDisposable +// , INativeList { [NativeDisableUnsafePtrRestriction] public readonly int* Ptr; @@ -42,7 +44,7 @@ internal unsafe struct UnsafeIntList : IDisposable public void AddNoResize(int value) { this.ListData().AddNoResize(value); } public void AddRangeNoResize(void* ptr, int length) { this.ListData().AddRangeNoResize(ptr, length); } public void AddRangeNoResize(UnsafeIntList src) { this.ListData().AddRangeNoResize(src.ListData()); } - public void Add(int value) { this.ListData().Add(value); } + public void Add(in int value) { this.ListData().Add(value); } public void AddRange(UnsafeIntList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } @@ -106,7 +108,9 @@ public unsafe int[] Items [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(UnsafeUintListDebugView))] - internal unsafe struct UnsafeUintList : IDisposable + internal unsafe struct UnsafeUintList + : INativeDisposable +// , INativeList { [NativeDisableUnsafePtrRestriction] public readonly uint* Ptr; @@ -129,7 +133,7 @@ internal unsafe struct UnsafeUintList : IDisposable public void AddNoResize(uint value) { this.ListData().AddNoResize(value); } public void AddRangeNoResize(void* ptr, int length) { this.ListData().AddRangeNoResize(ptr, length); } public void AddRangeNoResize(UnsafeUintList src) { this.ListData().AddRangeNoResize(src.ListData()); } - public void Add(uint value) { this.ListData().Add(value); } + public void Add(in uint value) { this.ListData().Add(value); } public void AddRange(UnsafeUintList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } @@ -193,7 +197,9 @@ public unsafe uint[] Items [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(UnsafeChunkPtrListDebugView))] - internal unsafe struct UnsafeChunkPtrList : IDisposable + internal unsafe struct UnsafeChunkPtrList + : INativeDisposable +// , INativeList { [NativeDisableUnsafePtrRestriction] public readonly Chunk** Ptr; @@ -213,7 +219,7 @@ internal unsafe struct UnsafeChunkPtrList : IDisposable public void TrimExcess() { this.ListData().TrimExcess(); } public int IndexOf(Chunk* value) { return this.ListData().IndexOf(value); } public bool Contains(Chunk* value) { return this.ListData().Contains(value); } - public void Add(Chunk* value) { this.ListData().Add(value); } + public void Add(in Chunk* value) { this.ListData().Add(value); } public void AddRange(UnsafeChunkPtrList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } @@ -279,7 +285,9 @@ public unsafe Chunk[] Items } [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(UnsafeArchetypePtrListDebugView))] - internal unsafe struct UnsafeArchetypePtrList : IDisposable + internal unsafe struct UnsafeArchetypePtrList + : INativeDisposable +// , INativeList { [NativeDisableUnsafePtrRestriction] public readonly Archetype** Ptr; @@ -299,7 +307,7 @@ internal unsafe struct UnsafeArchetypePtrList : IDisposable public void TrimExcess() { this.ListData().TrimExcess(); } public int IndexOf(Archetype* value) { return this.ListData().IndexOf(value); } public bool Contains(Archetype* value) { return this.ListData().Contains(value); } - public void Add(Archetype* value) { this.ListData().Add(value); } + public void Add(in Archetype* value) { this.ListData().Add(value); } public void AddRange(UnsafeArchetypePtrList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } @@ -365,7 +373,9 @@ public unsafe Archetype[] Items } [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(UnsafeEntityQueryDataPtrListDebugView))] - internal unsafe struct UnsafeEntityQueryDataPtrList : IDisposable + internal unsafe struct UnsafeEntityQueryDataPtrList + : INativeDisposable +// , INativeList { [NativeDisableUnsafePtrRestriction] public readonly EntityQueryData** Ptr; @@ -385,7 +395,7 @@ internal unsafe struct UnsafeEntityQueryDataPtrList : IDisposable public void TrimExcess() { this.ListData().TrimExcess(); } public int IndexOf(EntityQueryData* value) { return this.ListData().IndexOf(value); } public bool Contains(EntityQueryData* value) { return this.ListData().Contains(value); } - public void Add(EntityQueryData* value) { this.ListData().Add(value); } + public void Add(in EntityQueryData* value) { this.ListData().Add(value); } public void AddRange(UnsafeEntityQueryDataPtrList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } diff --git a/Unity.Entities/UnsafeList.tt b/Unity.Entities/UnsafeList.tt index 814e8a47..3113bb15 100644 --- a/Unity.Entities/UnsafeList.tt +++ b/Unity.Entities/UnsafeList.tt @@ -30,7 +30,9 @@ namespace Unity.Entities #> [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(Unsafe<#=typeName#>ListDebugView))] - internal unsafe struct Unsafe<#=typeName#>List : IDisposable + internal unsafe struct Unsafe<#=typeName#>List + : INativeDisposable +// , INativeList<<#=type#>> { [NativeDisableUnsafePtrRestriction] public readonly <#=type#>* Ptr; @@ -53,7 +55,7 @@ namespace Unity.Entities public void AddNoResize(<#=type#> value) { this.ListData().AddNoResize(value); } public void AddRangeNoResize(void* ptr, int length) { this.ListData().AddRangeNoResize<<#=type#>>(ptr, length); } public void AddRangeNoResize(Unsafe<#=typeName#>List src) { this.ListData().AddRangeNoResize<<#=type#>>(src.ListData()); } - public void Add(<#=type#> value) { this.ListData().Add(value); } + public void Add(in <#=type#> value) { this.ListData().Add(value); } public void AddRange(Unsafe<#=typeName#>List src) { this.ListData().AddRange<<#=type#>>(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack<<#=type#>>(index); } @@ -126,7 +128,9 @@ namespace Unity.Entities #> [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] [DebuggerTypeProxy(typeof(Unsafe<#=typeName#>PtrListDebugView))] - internal unsafe struct Unsafe<#=typeName#>PtrList : IDisposable + internal unsafe struct Unsafe<#=typeName#>PtrList + : INativeDisposable +// , INativeList<<#=type#>> { [NativeDisableUnsafePtrRestriction] public readonly <#=type#>** Ptr; @@ -146,7 +150,7 @@ namespace Unity.Entities public void TrimExcess() { this.ListData().TrimExcess(); } public int IndexOf(<#=type#>* value) { return this.ListData().IndexOf(value); } public bool Contains(<#=type#>* value) { return this.ListData().Contains(value); } - public void Add(<#=type#>* value) { this.ListData().Add(value); } + public void Add(in <#=type#>* value) { this.ListData().Add(value); } public void AddRange(Unsafe<#=typeName#>PtrList src) { this.ListData().AddRange(src.ListData()); } public void RemoveAtSwapBack(int index) { this.ListData().RemoveAtSwapBack(index); } diff --git a/Unity.Entities/World.cs b/Unity.Entities/World.cs index 8ca160f5..26cd9959 100644 --- a/Unity.Entities/World.cs +++ b/Unity.Entities/World.cs @@ -160,6 +160,48 @@ internal SystemHandle(ushort slot, ushort version, uint worldSeqNo) public static implicit operator SystemHandleUntyped(SystemHandle self) => self.MHandle; } + internal struct WorldUnmanagedImpl + { + public TimeData CurrentTime; + } + + public unsafe struct WorldUnmanaged + { + private WorldUnmanagedImpl* m_Impl; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + private AtomicSafetyHandle m_Safety; +#endif + public ref TimeData CurrentTime => ref GetImpl()->CurrentTime; + + internal void Create() + { + m_Impl = (WorldUnmanagedImpl*) UnsafeUtility.Malloc(sizeof(WorldUnmanagedImpl), 16, Allocator.Persistent); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_Safety = AtomicSafetyHandle.Create(); +#endif + } + + internal void Dispose() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckDeallocateAndThrow(m_Safety); + AtomicSafetyHandle.Release (m_Safety); +#endif + UnsafeUtility.Free(m_Impl, Allocator.Persistent); + m_Impl = null; + } + + private WorldUnmanagedImpl* GetImpl() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); +#endif + return m_Impl; + } + } + [DebuggerDisplay("{Name} - {Flags} (#{SequenceNumber})")] public unsafe partial class World : IDisposable { @@ -380,6 +422,10 @@ internal struct StateAllocLevel1 static int ms_SystemIDAllocator = 0; + private WorldUnmanaged m_Unmanaged; + + public WorldUnmanaged Unmanaged => m_Unmanaged; + internal static readonly SharedStatic ms_NextSequenceNumber = SharedStatic.GetOrCreate(); public readonly WorldFlags Flags; @@ -399,11 +445,9 @@ public override string ToString() public ulong SequenceNumber => m_SequenceNumber; - protected TimeData m_CurrentTime; + public ref TimeData Time => ref m_Unmanaged.CurrentTime; - public ref TimeData Time => ref m_CurrentTime; - - protected EntityQuery m_TimeSingletonQuery; + private EntityQuery m_TimeSingletonQuery; public World(string name) : this(name, WorldFlags.Simulation) {} @@ -413,6 +457,8 @@ public World(string name) : this(name, WorldFlags.Simulation) internal World(string name, WorldFlags flags) { + m_Unmanaged.Create(); + Systems = new NoAllocReadOnlyCollection(m_Systems); m_SequenceNumber = ms_NextSequenceNumber.Data++; @@ -467,6 +513,7 @@ public void Dispose() m_PendingUnmanagedDestroys.Dispose(); m_StateMemory.Dispose(); + m_Unmanaged.Dispose(); } public static void DisposeAllWorlds() @@ -500,13 +547,13 @@ protected Entity TimeSingleton public void SetTime(TimeData newTimeData) { EntityManager.SetComponentData(TimeSingleton, new WorldTime() {Time = newTimeData}); - m_CurrentTime = newTimeData; + this.Time = newTimeData; } public void PushTime(TimeData newTimeData) { var queue = EntityManager.GetBuffer(TimeSingleton); - queue.Add(new WorldTimeQueue() { Time = m_CurrentTime }); + queue.Add(new WorldTimeQueue() { Time = this.Time }); SetTime(newTimeData); } @@ -881,6 +928,17 @@ internal bool IsSystemValid(SystemHandleUntyped id) return ResolveSystemState(id) != null; } + internal SystemHandleUntyped InternalGetExistingUnmanagedSystem(long typeHash) + { + if (m_UnmanagedSlotByTypeHash.TryGetFirstValue(typeHash, out ushort handle, out _)) + { + var block = m_StateMemory.GetBlock(handle, out var subIndex); + return new SystemHandleUntyped(handle, block->Version[subIndex], (uint) m_SequenceNumber); + } + + throw new InvalidOperationException("system does not exist"); + } + internal SystemRef InternalGetExistingUnmanagedSystem() where T : struct, ISystemBase { if (m_UnmanagedSlotByTypeHash.TryGetFirstValue(BurstRuntime.GetHashCode64(), out ushort handle, out _)) @@ -912,9 +970,7 @@ private ushort AllocSlot(int structSize, long typeHash, out SystemState* statePt statePtr = m_StateMemory.Alloc(out var handle, out version, systemPtr, typeHash); UnsafeUtility.MemClear(statePtr, sizeof(SystemState)); - statePtr->Init(this, null); - statePtr->m_UnmanagedMetaIndex = metaIndex; - statePtr->m_SystemPtr = systemPtr; + statePtr->InitUnmanaged(this, metaIndex, systemPtr); ++Version; @@ -972,6 +1028,29 @@ internal SystemRef InternalCreateUnmanagedSystem() where T : struct, ISyst return new SystemRef(systemPtr, new SystemHandle(handle, version, (uint)m_SequenceNumber)); } +#if !UNITY_DOTSRUNTIME + internal SystemHandleUntyped InternalCreateUnmanagedSystem(Type t, long typeHash) + { + void* systemPtr = null; + ushort handle = AllocSlot(UnsafeUtility.SizeOf(t), typeHash, out var statePtr, out systemPtr, out var version); + + try + { + SystemBaseRegistry.CallOnCreate(statePtr); + } + catch (Exception ex) + { + Debug.LogException(ex); + FreeSlot(handle); + throw; + } + + m_UnmanagedSlotByTypeHash.Add(typeHash, handle); + + return new SystemHandleUntyped(handle, version, (uint)m_SequenceNumber); + } +#endif + internal void InternalDestroyUnmanagedSystem(SystemHandleUntyped sysHandle) { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -1016,13 +1095,34 @@ internal SystemRef GetOrCreateUnmanagedSystem() where T : struct, ISystemB } } +#if !UNITY_DOTSRUNTIME + internal SystemHandleUntyped GetOrCreateUnmanagedSystem(Type t) + { + long hash = BurstRuntime.GetHashCode64(t); + if (m_UnmanagedSlotByTypeHash.ContainsKey(hash)) + { + return InternalGetExistingUnmanagedSystem(hash); + } + else + { + return InternalCreateUnmanagedSystem(t, hash); + } + } + + internal SystemHandleUntyped CreateUnmanagedSystem(Type t) + { + long hash = BurstRuntime.GetHashCode64(t); + return InternalCreateUnmanagedSystem(t, hash); + } +#endif + internal Type GetTypeOfUnmanagedSystem(SystemHandleUntyped systemHandleUntyped) { SystemState* s = ResolveSystemState(systemHandleUntyped); if (s != null) { - return SystemBaseRegistry.GetStructType(s->m_UnmanagedMetaIndex); + return SystemBaseRegistry.GetStructType(s->UnmanagedMetaIndex); } return null; @@ -1073,6 +1173,13 @@ internal static SystemRef GetOrCreateSystem(this World self) where T : str return self.GetOrCreateUnmanagedSystem(); } +#if !UNITY_DOTSRUNTIME + internal static SystemHandleUntyped GetOrCreateUnmanagedSystem(this World self, Type unmanagedType) + { + return self.GetOrCreateUnmanagedSystem(unmanagedType); + } +#endif + internal static void DestroySystem(this World self, SystemHandleUntyped sysHandle) { self.InternalDestroyUnmanagedSystem(sysHandle); diff --git a/Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs b/Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs deleted file mode 100644 index 29105256..00000000 --- a/Unity.Scenes.Editor.Tests/EmbeddedPackageOnlyTestAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using NUnit.Framework; -using NUnit.Framework.Interfaces; -using NUnit.Framework.Internal; -using UnityEngine; - -namespace Unity.Entities.Tests -{ - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] - class EmbeddedPackageOnlyTestAttribute : NUnitAttribute, IApplyToTest - { - public void ApplyToTest(Test test) - { - var assembly = test.Method?.TypeInfo?.Assembly; - if (assembly == null) - { - Debug.LogError($"The {nameof(EmbeddedPackageOnlyTestAttribute)} attribute can only be applied to tests in an assembly."); - return; - } - - var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(assembly); - if (package == null) - { - Debug.LogError( - $"The {nameof(EmbeddedPackageOnlyTestAttribute)} attribute can only be applied to tests in a package."); - return; - } - - if (package.source != UnityEditor.PackageManager.PackageSource.Embedded) - { - test.RunState = RunState.Ignored; - test.Properties.Add(PropertyNames.SkipReason, "Only runs in the editor when this package is embedded."); - } - } - } -} diff --git a/Unity.Scenes.Editor.Tests/LiveLinkEditorConnectionTests.cs b/Unity.Scenes.Editor.Tests/LiveLinkEditorConnectionTests.cs index d23ee1d7..6351a17c 100644 --- a/Unity.Scenes.Editor.Tests/LiveLinkEditorConnectionTests.cs +++ b/Unity.Scenes.Editor.Tests/LiveLinkEditorConnectionTests.cs @@ -303,7 +303,7 @@ IEnumerable WaitForAssets(Dictionary outHashesByGUID, int play } else { - Assert.IsTrue(!outHashesByGUID.TryGetValue(assets[i].GUID, out var hash) || !hash.IsValid, $"Received a valid hash for GUID {assets[i].GUID} ({path}), but we previously already received the hash {hash}."); + Assert.IsTrue(!outHashesByGUID.TryGetValue(assets[i].GUID, out var hash) || !hash.IsValid, $"Received a valid hash {assets[i].TargetHash} for GUID {assets[i].GUID} ({path}), but we previously already received the hash {hash}."); if (!IsSpecialGUID(assets[i].GUID)) Assert.AreEqual(GetLiveLinkArtifactHash(assets[i].GUID), assets[i].TargetHash, $"Hash mismatch for GUID {assets[i].GUID} ({path})"); } @@ -541,8 +541,30 @@ static unsafe string ReadArtifactName(byte[] msg) } } + static void EnsureSubSceneImported(in SubSceneGUID subSceneGUID) + { + var buildTarget = EditorUserBuildSettings.activeBuildTarget; + + // First SubScene artifact hash + var subSceneHash = EntityScenesPaths.GetSubSceneArtifactHash(subSceneGUID.Guid, subSceneGUID.BuildConfigurationGuid, ImportMode.Synchronous); + + // Gather all top level dependencies + var sceneDependencies = LiveLinkBuildPipeline.GetSubSceneDependencies(subSceneHash); + + foreach (var sceneDependency in sceneDependencies) + { + var sceneDependencyHash = LiveLinkBuildPipeline.CalculateTargetHash(sceneDependency.GUID, buildTarget, ImportMode.Synchronous); + + LiveLinkBuildPipeline.CalculateTargetDependencies(sceneDependencyHash, buildTarget, out ResolvedAssetID[] assetDependencies, ImportMode.Asynchronous, sceneDependency.GUID); + + foreach (var assetDependency in assetDependencies) + { + LiveLinkBuildPipeline.CalculateTargetHash(assetDependency.GUID, buildTarget, ImportMode.Synchronous); + } + } + } + [UnityTest] - [Ignore("This test is unstable. The AssetDatabase contains race conditions which can deadlock the editor. Jira: DOTS-1914")] public IEnumerator EditorSendsSubSceneEndToEnd() { DoConnect(); @@ -554,12 +576,36 @@ public IEnumerator EditorSendsSubSceneEndToEnd() BuildConfigurationGuid = s_LiveLinkBuildConfigGuid, }; Assert.IsTrue(subSceneGuid.Guid.IsValid); + + EnsureSubSceneImported(subSceneGuid); + var sentSubSceneId = new ResolvedSubSceneID { SubSceneGUID = subSceneGuid, TargetHash = EntityScenesPaths.GetSubSceneArtifactHash(subSceneGuid.Guid, subSceneGuid.BuildConfigurationGuid, ImportMode.Synchronous) }; + s_Connection.PostMessageArray(1, LiveLinkMsg.PlayerRequestSubSceneTargetHash, new[] { subSceneGuid }); + foreach (var v in WaitForMessage()) + yield return v; + + var subSceneTargetHashMsg = AssertNextMessage(LiveLinkMsg.EditorResponseSubSceneTargetHash, 1); + var receivedResolvedSubScenes = subSceneTargetHashMsg.EventArgs.ReceiveArray(); + Assert.AreEqual(1, receivedResolvedSubScenes.Length, "Expected only 1 resolved SubScene from this request"); + Assert.AreEqual(sentSubSceneId.SubSceneGUID, receivedResolvedSubScenes[0].SubSceneGUID, "Received wrong SubScene GUID"); + Assert.AreEqual(sentSubSceneId.TargetHash, receivedResolvedSubScenes[0].TargetHash, "Received different SubScene hash"); + + // Now get asset dependency hashes +#if !UNITY_DISABLE_MANAGED_COMPONENTS + var subSceneAssetHashesMsg = AssertNextMessage(LiveLinkMsg.EditorResponseAssetTargetHash, 1); + var receivedAssetHashes = subSceneAssetHashesMsg.EventArgs.ReceiveArray(); + + Assert.Greater(receivedAssetHashes.Length, 0, "Runtime references are empty!"); + + Assert.IsTrue(receivedAssetHashes.Any(x => x.GUID == (Hash128)s_TempMaterialGuid), "Runtime references does not include material"); + Assert.IsTrue(receivedAssetHashes.Any(x => x.GUID == (Hash128)s_TempTextureGuid), "Runtime references does not include texture"); +#endif + s_Connection.PostMessage(1, LiveLinkMsg.PlayerRequestSubSceneForGUID, sentSubSceneId); foreach (var v in WaitForMessage()) yield return v; @@ -575,16 +621,8 @@ public IEnumerator EditorSendsSubSceneEndToEnd() } var msg = AssertNextMessage(LiveLinkMsg.EditorResponseSubSceneForGUID, 1); - ReadSubSceneResponse(msg.Data, out var receivedSubSceneId, out var runtimeGlobalObjectIds); + var receivedSubSceneId = msg.EventArgs.Receive(); Assert.AreEqual(sentSubSceneId, receivedSubSceneId); - using (runtimeGlobalObjectIds) - { -#if !UNITY_DISABLE_MANAGED_COMPONENTS - Assert.Greater(runtimeGlobalObjectIds.Length, 0, "Runtime references are empty!"); - Assert.IsTrue(runtimeGlobalObjectIds.Any(x => x.AssetGUID == (Hash128)s_TempMaterialGuid), "Runtime references does not include material"); - Assert.IsTrue(runtimeGlobalObjectIds.Any(x => x.AssetGUID == (Hash128)s_TempTextureGuid), "Runtime references does not include texture"); -#endif - } var targetHash = ((UnityEngine.Hash128)receivedSubSceneId.TargetHash).ToString(); AssertFileReceived($"{targetHash}.0.entities"); diff --git a/Unity.Scenes.Editor.Tests/LiveLinkEditorTests.cs b/Unity.Scenes.Editor.Tests/LiveLinkEditorTests.cs index c0ad87be..75d1afac 100644 --- a/Unity.Scenes.Editor.Tests/LiveLinkEditorTests.cs +++ b/Unity.Scenes.Editor.Tests/LiveLinkEditorTests.cs @@ -10,7 +10,6 @@ using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; -using UnityEngine.LowLevel; using UnityEngine.SceneManagement; using UnityEngine.TestTools; using Object = UnityEngine.Object; @@ -42,7 +41,6 @@ class LiveLinkEditorTests [SerializeField] TestWithCustomDefaultGameObjectInjectionWorld m_DefaultWorld; - private PlayerLoopSystem m_PrevPlayerLoop; [SerializeField] TestWithSubScenes m_SubSceneTest; [SerializeField] @@ -71,7 +69,6 @@ public void SetUp() // Create a temporary folder for test assets m_Assets.SetUp(); - m_PrevPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); m_DefaultWorld.Setup(); m_SubSceneTest.Setup(); m_WasLiveLinkEnabled = SubSceneInspectorUtility.LiveLinkEnabledInEditMode; @@ -93,7 +90,6 @@ public void OneTimeTearDown() EditorSettings.enterPlayModeOptions = m_EnterPlayModeOptions; EditorSettings.enterPlayModeOptionsEnabled = m_UseEnterPlayerModeOptions; SubSceneInspectorUtility.LiveLinkEnabledInEditMode = m_WasLiveLinkEnabled; - PlayerLoop.SetPlayerLoop(m_PrevPlayerLoop); } static string AssetPath(string name) => "Packages/com.unity.entities/Unity.Scenes.Editor.Tests/Assets/" + name; diff --git a/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs new file mode 100644 index 00000000..ce3fa55e --- /dev/null +++ b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; +using Unity.Build; +using Unity.Build.Classic; +using Unity.Build.Common; +using Unity.Entities; +using Unity.Entities.Hybrid.Tests; +using Unity.Entities.Tests; +using Unity.Scenes.Editor; +using Unity.Scenes.Editor.Tests; +using UnityEditor; +using UnityEditor.Build.Content; +using UnityEngine; +using UnityEngine.TestTools; +using Hash128 = Unity.Entities.Hash128; + +public class SubSceneDeduplicationTests +{ + static object[] DedupeSubScenesTestData = + { + new object[] + { + "Simple", + new(string, int[])[] + { + ("t1.png", new int[] {}), //texture with no refs + ("a1.asset", new int[] { 0 }), //asset with ref to t1.png + }, + new(int, (int, int[]))[] + { + (0, (0, new int[] { 1 })), //section 0, in subscene 0, with ref to a1.asset + (0, (1, new int[] { 1 })), //section 1, in subscene 0, with ref to a1.asset + }, + (1, new int[] {1, 1}) //expecting 1 dedupe bundle, each section has 1 dependency + }, + new object[] + { + "Slightly Complex", + new(string, int[])[] + { + ("t1.png", new int[] {}), //texture with no refs + ("t2.png", new int[] {}), //texture with no refs + ("a1.asset", new int[] { 0, 1 }), //asset with ref to t1.png & t2.png + ("a2.asset", new int[] { 0 }), //asset with ref to t1.png + }, + new(int, (int, int[]))[] + { + (0, (0, new int[] { 2 })), //section 0, in subscene 0, with ref to a1.asset + (0, (1, new int[] { 3 })), //section 1, in subscene 0, with ref to a2.asset + }, + (1, new int[] {1, 1}) //expecting 1 dedupe bundle, each section has 1 dependency + }, + new object[] + { + "More Complex", + new(string, int[])[] + { + ("t1.png", new int[] {}), //texture with no refs + ("t2.png", new int[] {}), //texture with no refs + ("t3.png", new int[] {}), //texture with no refs + ("a1.asset", new int[] { 0, 1 }), //asset with ref to t1.png & t2.png + ("a2.asset", new int[] { 0 }), //asset with ref to t1.png + ("a3.asset", new int[] { 1, 2 }), //asset with ref to t2.png & t3.png + }, + new(int, (int, int[]))[] + { + (0, (0, new int[] { 3 })), //section 0, in subscene 0, with ref to a1.asset + (0, (1, new int[] { 4 })), //section 1, in subscene 0, with ref to a2.asset + (1, (0, new int[] { 5 })), //section 0, in subscene 1, with ref to a3.asset + }, + (2, new int[] {2, 1, 1}) //expecting 2 dedupe bundles + }, + new object[] + { + "Most Complex", + new(string, int[])[] + { + ("t1.png", new int[] {}), //texture with no refs + ("t2.png", new int[] {}), //texture with no refs + ("t3.png", new int[] {}), //texture with no refs + ("t4.png", new int[] {}), //texture with no refs + ("a1.asset", new int[] { 0 }), //asset with ref to t1.png + ("a2.asset", new int[] { 1 }), //asset with ref to t2.png + ("a3.asset", new int[] { 2 }), //asset with ref to t3.png + ("a4.asset", new int[] { 3 }), //asset with ref to t4.png + }, + new(int, (int, int[]))[] + { + (0, (0, new int[] { 4, 5 })), //section 0, in subscene 0, with ref to a1.asset & t4.png + (0, (1, new int[] { 5 })), //section 1, in subscene 0, with ref to a2.asset + (1, (0, new int[] { 4, 5, 6, 7 })), //section 0, in subscene 1, with ref to a3.asset + (1, (1, new int[] { 6, 7 })), //section 0, in subscene 1, with ref to t4.png + (1, (2, new int[] { 4, 6, 7 })), //section 0, in subscene 1, with ref to t4.png + }, + (3, new int[] {2, 1, 3, 1, 2}) //expecting 3 dedupe bundles + }, + }; + + string tempAssetDir; + [OneTimeSetUp] + public void OneTimeSetUp() + { + var subFolderPath = $"TestAssets_{GUID.Generate()}"; + if (!AssetDatabase.IsValidFolder(subFolderPath)) + { + var folderGUID = AssetDatabase.CreateFolder("Assets", subFolderPath); + tempAssetDir = AssetDatabase.GUIDToAssetPath(folderGUID); + } + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + if (AssetDatabase.IsValidFolder(tempAssetDir)) + AssetDatabase.DeleteAsset(tempAssetDir); + } + + [TestCaseSource(nameof(DedupeSubScenesTestData))] + public void SubSceneBundleDedupeTest(string descr, (string, int[])[] objects, (int, (int, int[]))[] sections, (int, int[]) expected) + { + var allObjects = objects.Select(o => (GetObjectIdentifier(tempAssetDir, o.Item1), o.Item2)).ToArray(); + var sectionToIncludedObjects = new Dictionary(); + var subScenes = new List(); + var sectionsList = new List(); + foreach (var s in sections) + { + if (subScenes.Count <= s.Item1) + subScenes.Add(new Hash128(GUID.Generate().ToString())); + var sec = new SceneSection() { SceneGUID = subScenes[s.Item1], Section = s.Item2.Item1 }; + sectionsList.Add(sec); + sectionToIncludedObjects.Add(sec, CreateTestDependencyInfo(s.Item2.Item2.Select(i => allObjects[i].Item1), allObjects)); + } + + var dependencyMapping = new Dictionary>>(); + var layout = new Dictionary>(); + + SubSceneBuildCode.CreateAssetLayoutData(sectionToIncludedObjects, dependencyMapping, layout); + + Assert.AreEqual(expected.Item1, layout.Count, $"{descr} - Did not create the expected bundle layout"); + Assert.AreEqual(subScenes.Count, dependencyMapping.Count, $"{descr} - Did not create the expected number of mappings - this should be equal to the number of sub scenes"); + for (int i = 0; i < sectionsList.Count; i++) + { + var ssData = dependencyMapping[sectionsList[i].SceneGUID]; + var depsForSection = ssData[sectionsList[i]]; + Assert.AreEqual(expected.Item2[i], depsForSection.Count, $"{descr} - Sub scene defined at index {i} did not get the expected number of dependency bundles."); + } + } + + private SubSceneBuildCode.SectionDependencyInfo CreateTestDependencyInfo(IEnumerable ids, (ObjectIdentifier, int[])[] allObjects) + { + var deps = GetObjectTestDependencies(ids, allObjects); + var paths = deps.Select(d => AssetDatabase.GUIDToAssetPath(d.guid.ToString())).ToArray(); + var types = paths.Select(p => AssetDatabase.GetMainAssetTypeAtPath(p)).ToArray(); + return new SubSceneBuildCode.SectionDependencyInfo() + { + Dependencies = deps, + Types = types, + Paths = paths + }; + } + + static ObjectIdentifier GetObjectIdentifier(string dir, string path) + { + path = Path.Combine(dir, path); + if (!File.Exists(path)) + { + var ext = Path.GetExtension(path); + switch (ext) + { + case ".png": + CreateTestTexture(path); + break; + case ".asset": + CreateTestAsset(path); + break; + } + } + var ids = ContentBuildInterface.GetPlayerObjectIdentifiersInAsset(new GUID(AssetDatabase.AssetPathToGUID(path)), EditorUserBuildSettings.activeBuildTarget); + return ids.Length == 0 ? default : ids[0]; + } + + private static void CreateTestAsset(string path) + { + Debug.Log($"Creating asset at path {path}"); + var so = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(so, path); + } + + private static void CreateTestTexture(string path) + { + Debug.Log($"Creating texture at path {path}"); + var tex = new Texture2D(8, 8); + File.WriteAllBytes(path, tex.EncodeToPNG()); + var text = AssetDatabase.LoadAssetAtPath(path); + AssetDatabase.ImportAsset(path, ImportAssetOptions.Default); + } + + static ObjectIdentifier[] GetObjectTestDependencies(IEnumerable ids, (ObjectIdentifier, int[])[] ad) + { + var deps = new HashSet(); + foreach (var id in ids) + { + foreach (var a in ad) + if (a.Item1.Equals(id)) + foreach (var d in a.Item2) + deps.Add(ad[d].Item1); + } + return deps.ToArray(); + } + + [Test] + public void SubSceneDeduplicationValidation_WithNullInput_ReturnsFalse() + { + Assert.IsFalse(SubSceneBuildCode.ValidateInput(null, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithInvalidSceneInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = default }, new SubSceneBuildCode.SectionDependencyInfo()); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithInvalidSectionInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = -1 }, new SubSceneBuildCode.SectionDependencyInfo()); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithNullDependenciesInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo()); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithNullTypesInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[1] }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithNullPathsInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[1], Types = new Type[2] }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithMismatchedDataInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[1], Types = new Type[2], Paths = new string[3] }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithInvalidGUIDInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[] { new ObjectIdentifier() }, Types = new Type[] { typeof(object) }, Paths = new string[] { "notEmpty" } }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithNullTypeInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[] { GetObjectIdentifier(tempAssetDir, "blah.png") }, Types = new Type[] { null }, Paths = new string[] { "notEmpty" } }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } + + [Test] + public void SubSceneDeduplicationValidation_WithEmptyPathInput_ReturnsFalse() + { + var input = new Dictionary(); + input.Add(new SceneSection() { SceneGUID = new Hash128(1, 2, 3, 4), Section = 0 }, new SubSceneBuildCode.SectionDependencyInfo() { Dependencies = new ObjectIdentifier[] { GetObjectIdentifier(tempAssetDir, "blah.png") }, Types = new Type[] { typeof(object) }, Paths = new string[] { "" } }); + Assert.IsFalse(SubSceneBuildCode.ValidateInput(input, out var error)); + Assert.IsNotNull(error); + } +} diff --git a/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs.meta b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs.meta new file mode 100644 index 00000000..5c589d20 --- /dev/null +++ b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 77fd4db18f15fc94ebf0c50fa31493d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity.Scenes.Editor.Tests/Unity.Scenes.Editor.Tests.asmdef b/Unity.Scenes.Editor.Tests/Unity.Scenes.Editor.Tests.asmdef index c4f247f2..9cf8a28d 100644 --- a/Unity.Scenes.Editor.Tests/Unity.Scenes.Editor.Tests.asmdef +++ b/Unity.Scenes.Editor.Tests/Unity.Scenes.Editor.Tests.asmdef @@ -12,6 +12,8 @@ "Unity.Entities.TestComponents", "Unity.Mathematics", "Unity.Build", + "Unity.Build.Classic", + "Unity.Build.Common", "Unity.Collections" ], "includePlatforms": [ diff --git a/Unity.Scenes.Editor/EditorEntityScenes.cs b/Unity.Scenes.Editor/EditorEntityScenes.cs index b071ef55..1282afc1 100644 --- a/Unity.Scenes.Editor/EditorEntityScenes.cs +++ b/Unity.Scenes.Editor/EditorEntityScenes.cs @@ -34,6 +34,7 @@ internal struct WriteEntitySceneSettings public BuildAssemblyCache BuildAssemblyCache; public string OutputPath; public Codec Codec; + public ConversionJournalData JournalData; #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value } @@ -185,10 +186,14 @@ internal static SceneSectionData[] ConvertAndWriteEntitySceneInternal(Scene scen // Optimizing and writing the scene is done here to include potential log messages in the conversion log. EntitySceneOptimization.Optimize(world); int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(settings.BuildConfiguration); + writeEntitySettings.JournalData = mappingSystem.JournalData; sections = WriteEntitySceneInternal(world.EntityManager, settings.SceneGUID, scene.name, settings.AssetImportContext, framesToRetainBlobAssets, sectionRefObjs, writeEntitySettings); - var journalData = mappingSystem.JournalData.SelectLogEventsOrdered().ToList(); + if (writeEntitySettings.IsDotsRuntime && sectionRefObjs.Count != 0) + mappingSystem.JournalData.RecordExceptionEvent(null, new ArgumentException("We are serializing a world that contains UnityEngine.Object references which are not supported in Dots Runtime.")); + // Save the log of issues that happened during conversion + var journalData = mappingSystem.JournalData.SelectLogEventsOrdered().ToList(); WriteConversionLog(settings.SceneGUID, journalData, settings.AssetImportContext, writeEntitySettings.OutputPath); }; @@ -211,6 +216,22 @@ public static SceneSectionData[] WriteEntityScene(EntityManager entityManager, H return WriteEntitySceneInternal(entityManager, sceneGUID, sceneName, importContext, framesToRetainBlobAssets, sectionRefObjs, new WriteEntitySceneSettings()); } + static void AddExportedTypesToJournalData(WriteEntitySceneSettings writeEntitySceneSettings, NativeArray typeInfos) + { + if (!writeEntitySceneSettings.IsDotsRuntime) + return; + + if (typeInfos.Length > 0) + { + writeEntitySceneSettings.JournalData.RecordLogEvent(null, LogType.Log, "::Exported Types (by stable hash)::"); + foreach (var componentType in typeInfos) + { + var typeInfo = TypeManager.GetTypeInfo(componentType.TypeIndex); + writeEntitySceneSettings.JournalData.RecordLogEvent(null, LogType.Log, $"0x{typeInfo.StableTypeHash:x16} - {typeInfo.StableTypeHash,22} - {typeInfo.Type.FullName}"); + } + } + } + internal static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, Hash128 sceneGUID, string sceneName, AssetImportContext importContext, int framesToRetainBlobAssets, List sectionRefObjs, WriteEntitySceneSettings writeEntitySceneSettings) @@ -237,13 +258,17 @@ internal static SceneSectionData[] WriteEntitySceneInternal(EntityManager entity { var type = TypeManager.GetTypeInfo(componentType.TypeIndex).Type; if (!writeEntitySceneSettings.BuildAssemblyCache.HasType(type)) - throw new InvalidOperationException( - $"The {type.Name} component is defined in the {type.Assembly.GetName().Name} assembly, but that assembly is not referenced by the current build configuration. Either add it as a reference, or ensure that the conversion process that is adding that component does not run."); + writeEntitySceneSettings.JournalData.RecordExceptionEvent(null, new ArgumentException($"The {type.Name} component is defined in the {type.Assembly.GetName().Name} assembly, but that assembly is not referenced by the current build configuration. Either add it as a reference, or ensure that the conversion process that is adding that component does not run.")); } if(importContext != null) TypeDependencyCache.AddDependency(importContext, componentType); } } + //Add exported types and assets to the journal data + using (var types = allTypes.GetKeyArray(Allocator.Temp)) + { + AddExportedTypesToJournalData(writeEntitySceneSettings, types); + } } if (importContext != null) TypeDependencyCache.AddAllSystemsDependency(importContext); diff --git a/Unity.Scenes.Editor/LiveLink/LiveLinkAssetBundleBuildSystem.cs b/Unity.Scenes.Editor/LiveLink/LiveLinkAssetBundleBuildSystem.cs index b7ef2c74..23dcb553 100644 --- a/Unity.Scenes.Editor/LiveLink/LiveLinkAssetBundleBuildSystem.cs +++ b/Unity.Scenes.Editor/LiveLink/LiveLinkAssetBundleBuildSystem.cs @@ -49,35 +49,6 @@ public void RequestSubSceneByGUID(MessageEventArgs args) SendSubScene(subSceneId, args.playerId); } - NativeArray GetSubSceneDependencies(Hash128 artifactHash) - { - //@TODO: should be based on connection / BuildSetting - var buildTarget = EditorUserBuildSettings.activeBuildTarget; - - var resolvedDependencies = default(NativeArray); - AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths); - var loadPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesAssetDependencyGUIDs); - - if (loadPath != null && File.Exists(loadPath)) - { - using (var reader = new StreamBinaryReader(loadPath)) - { - var len = reader.ReadInt(); - var dependencies = new NativeArray(len, Allocator.Temp); - reader.ReadArray(dependencies, len); - - resolvedDependencies = new NativeArray(dependencies.Length, Allocator.Temp); - for(int i = 0; i < dependencies.Length; i++) - { - var targetHash = LiveLinkBuildPipeline.CalculateTargetHash(dependencies[i], buildTarget, ImportMode.NoImport); - resolvedDependencies[i] = new ResolvedAssetID { GUID = dependencies[i], TargetHash = targetHash }; - } - } - } - - return resolvedDependencies; - } - void RequestSubSceneTargetHash(MessageEventArgs args) { //@TODO: should be based on connection / BuildSetting @@ -98,7 +69,7 @@ void RequestSubSceneTargetHash(MessageEventArgs args) { resolvedScenes.Add(new ResolvedSubSceneID {SubSceneGUID = subScene, TargetHash = targetHash}); - var sceneDependencies = GetSubSceneDependencies(targetHash); + var sceneDependencies = LiveLinkBuildPipeline.GetSubSceneDependencies(targetHash); foreach (var sceneDependency in sceneDependencies) { assetDependencies.Add(sceneDependency); @@ -329,7 +300,7 @@ void SendSubScene(ResolvedSubSceneID subSceneId, int playerId) if (sceneMetaData.Sections[i].ObjectReferenceCount > 0) { var scriptedObjPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex); - var bundleName = $"{(UnityEngine.Hash128)subSceneId.TargetHash}.{sectionIndex}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnitObjectReferencesBundle)}"; + var bundleName = $"{(UnityEngine.Hash128)subSceneId.TargetHash}.{sectionIndex}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferencesBundle)}"; var tempPath = Path.GetTempFileName(); AssetBundleTypeCache.RegisterMonoScripts(); @@ -480,7 +451,7 @@ void DetectChangedAssets() LiveLinkMsg.LogInfo("Detected subscene change: " + subScene.Key); changedSubScenes.Add(new ResolvedSubSceneID{ SubSceneGUID = subScene.Key, TargetHash = targetHash }); - var sceneDependencies = GetSubSceneDependencies(targetHash); + var sceneDependencies = LiveLinkBuildPipeline.GetSubSceneDependencies(targetHash); foreach (var sceneDependency in sceneDependencies) { assetDependencies.Add(sceneDependency); diff --git a/Unity.Scenes.Editor/LiveLink/LiveLinkBuildImporter.cs b/Unity.Scenes.Editor/LiveLink/LiveLinkBuildImporter.cs index a4e0052b..769401a8 100644 --- a/Unity.Scenes.Editor/LiveLink/LiveLinkBuildImporter.cs +++ b/Unity.Scenes.Editor/LiveLink/LiveLinkBuildImporter.cs @@ -101,9 +101,6 @@ internal static Hash128 GetHash(string guid, BuildTarget target, ImportMode impo return AssetDatabaseCompatibility.GetArtifactHash(guid, typeof(LiveLinkBuildImporter), importMode); } - [Obsolete("LiveLinkBuildImport.GetDependencies has been deprecated. It will not be supported in the future. (RemovedAfter 2020-06-13).")] - public static Hash128[] GetDependencies(Hash128 artifactHash) => GetDependenciesInternal(artifactHash, new Hash128()); - // Recursive until new SBP APIs land in 2020.1 internal static Hash128[] GetDependenciesInternal(in Hash128 artifactHash, in Hash128 assetGUID) { @@ -140,9 +137,6 @@ internal static Hash128[] GetDependenciesInternal(in Hash128 artifactHash, in Ha return guids; } - [Obsolete("LiveLinkBuildImport.GetBundlePath has been deprecated. It will not be supported in the future. (RemovedAfter 2020-06-13).")] - public static string GetBundlePath(Hash128 artifactHash, GUID guid) => GetBundlePathInternal(artifactHash, guid); - internal static string GetBundlePathInternal(in Hash128 artifactHash, in Hash128 assetGUID) { AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths); diff --git a/Unity.Scenes.Editor/LiveLink/LiveLinkBuildPipeline.cs b/Unity.Scenes.Editor/LiveLink/LiveLinkBuildPipeline.cs index 1ac4a4bf..5611b1cf 100644 --- a/Unity.Scenes.Editor/LiveLink/LiveLinkBuildPipeline.cs +++ b/Unity.Scenes.Editor/LiveLink/LiveLinkBuildPipeline.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Unity.Collections; +using Unity.Entities.Serialization; using UnityEditor; using UnityEditor.Build.Content; using UnityEditor.Build.Pipeline; @@ -629,5 +631,39 @@ internal static void CalculateTargetDependencies(in Entities.Hash128 artifactHas dependencies = resolvedDependencies.ToArray(); } + + internal static NativeArray GetSubSceneDependencies(Hash128 artifactHash) + { + //@TODO: should be based on connection / BuildSetting + var buildTarget = EditorUserBuildSettings.activeBuildTarget; + + var resolvedDependencies = default(NativeArray); + AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths); + + if(paths == null || paths.Length == 0) + throw new InvalidOperationException($"SubScene had no valid artifact paths - hash {artifactHash}"); + + var loadPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesAssetDependencyGUIDs); + + if (loadPath != null && File.Exists(loadPath)) + { + using (var reader = new StreamBinaryReader(loadPath)) + { + var len = reader.ReadInt(); + var dependencies = new NativeArray(len, Allocator.Temp); + reader.ReadArray(dependencies, len); + + resolvedDependencies = new NativeArray(dependencies.Length, Allocator.Temp); + for(int i = 0; i < dependencies.Length; i++) + { + var targetHash = LiveLinkBuildPipeline.CalculateTargetHash(dependencies[i], buildTarget, ImportMode.NoImport); + resolvedDependencies[i] = new ResolvedAssetID { GUID = dependencies[i], TargetHash = targetHash }; + } + } + } + + return resolvedDependencies; + } + } } diff --git a/Unity.Scenes.Editor/SceneMetaDataImporter.cs b/Unity.Scenes.Editor/SceneMetaDataImporter.cs index 7867c5a4..5435ac53 100644 --- a/Unity.Scenes.Editor/SceneMetaDataImporter.cs +++ b/Unity.Scenes.Editor/SceneMetaDataImporter.cs @@ -25,6 +25,9 @@ public struct SceneMetaData public static Hash128[] GetSubSceneGuids(string guid) { var hash = AssetDatabaseCompatibility.GetArtifactHash(guid, typeof(SceneMetaDataImporter), ImportMode.Synchronous); + if (!hash.isValid) + throw new ArgumentException($"Invalid artifact hash from guid {guid}"); + AssetDatabaseCompatibility.GetArtifactPaths(hash, out string[] paths); var metaPath = paths.First(o => o.EndsWith("scenemeta")); diff --git a/Unity.Scenes.Editor/SubSceneBuildCode.cs b/Unity.Scenes.Editor/SubSceneBuildCode.cs index bcb2c4a6..bcd30181 100644 --- a/Unity.Scenes.Editor/SubSceneBuildCode.cs +++ b/Unity.Scenes.Editor/SubSceneBuildCode.cs @@ -2,12 +2,20 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Unity.Build; +using Unity.Build.Common; using Unity.Collections; using Unity.Entities; +using Unity.Entities.Serialization; using UnityEditor; using UnityEditor.Build.Content; using UnityEditor.Build.Pipeline; using UnityEditor.Build.Pipeline.Interfaces; +using UnityEditor.Build.Pipeline.Tasks; +using UnityEditor.Build.Pipeline.Utilities; +using UnityEditor.Build.Pipeline.Injector; +using UnityEditor.Build.Pipeline.WriteTypes; +using UnityEditor.Build.Utilities; using BuildCompression = UnityEngine.BuildCompression; using BuildPipeline = UnityEditor.BuildPipeline; @@ -41,12 +49,23 @@ public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[ throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(SubSceneBuildCode)} runs."); var content = new BundleBuildContent(new AssetBundleBuild[0]); - var bundleNames = new List(); + var bundleNames = new HashSet(); var subSceneGuids = scenePathsForBuild.SelectMany(scenePath => SceneMetaDataImporter.GetSubSceneGuids(AssetDatabase.AssetPathToGUID(scenePath))).Distinct().ToList(); + var subScenePaths = new Dictionary(); + var dependencyInputData = new Dictionary(); + var refExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences); + var headerExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader); + var binaryExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary); + + var group = BuildPipeline.GetBuildTargetGroup(target); + var parameters = new BundleBuildParameters(target, @group, buildWorkingDirectory) + { + BundleCompression = BuildCompression.LZ4Runtime + }; var requiresRefresh = false; var sceneBuildConfigGuids = new NativeArray(subSceneGuids.Count, Allocator.TempJob); - for (int i=0;i != sceneBuildConfigGuids.Length;i++) + for (int i = 0; i != sceneBuildConfigGuids.Length; i++) { sceneBuildConfigGuids[i] = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(subSceneGuids[i], new Hash128(buildConfigurationGuid), out var thisRequiresRefresh); requiresRefresh |= thisRequiresRefresh; @@ -57,8 +76,9 @@ public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[ var artifactHashes = new NativeArray(subSceneGuids.Count, Allocator.TempJob); AssetDatabaseCompatibility.ProduceArtifactsRefreshIfNecessary(sceneBuildConfigGuids, typeof(SubSceneImporter), artifactHashes); - for (int i=0;i != sceneBuildConfigGuids.Length;i++) + for (int i = 0; i != sceneBuildConfigGuids.Length; i++) { + var sceneGuid = subSceneGuids[i]; var sceneBuildConfigGuid = sceneBuildConfigGuids[i]; var artifactHash = artifactHashes[i]; @@ -69,21 +89,28 @@ public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[ { //@TODO: This looks like a workaround. Whats going on here? var ext = Path.GetExtension(artifactPath).Replace(".", ""); - - if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader)) + if (ext == headerExt) { foundEntityHeader = true; - var destinationFile = outputStreamingAssetsDirectory + "/" + EntityScenesPaths.RelativePathInStreamingAssetsFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1); - RegisterFileCopy(artifactPath, destinationFile); - } - if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary)) + if (!string.IsNullOrEmpty(artifactPaths.FirstOrDefault(a => a.EndsWith(refExt)))) + { + subScenePaths[sceneGuid] = artifactPath; + } + else + { + //if there are no reference bundles, then deduplication can be skipped + var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1); + DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile); + } + } + else if (ext == binaryExt) { - var destinationFile = outputStreamingAssetsDirectory + "/" + EntityScenesPaths.RelativePathInStreamingAssetsFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath)); - RegisterFileCopy(artifactPath, destinationFile); + var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath)); + DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile); } - if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences)) + if (ext == refExt) { content.CustomAssets.Add(new CustomContent { @@ -92,7 +119,8 @@ public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[ { var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(artifactPath); processor.GetObjectIdentifiersAndTypesForSerializedFile(artifactPath, out ObjectIdentifier[] objectIds, out Type[] types); - var bundlePath = EntityScenesPaths.GetLoadPath(sceneGuid, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex); + dependencyInputData[new SceneSection() { SceneGUID = sceneGuid, Section = sectionIndex }] = CreateDependencyInfo(objectIds, target, parameters.ScriptInfo); + var bundlePath = EntityScenesPaths.GetLoadPath(sceneGuid, EntityScenesPaths.PathType.EntitiesUnityObjectReferencesBundle, sectionIndex); var bundleName = Path.GetFileName(bundlePath); processor.CreateAssetEntryForObjectIdentifiers(objectIds, artifactPath, bundleName, bundleName, typeof(ReferencedUnityObjects)); bundleNames.Add(bundleName); @@ -113,20 +141,313 @@ public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[ if (content.CustomAssets.Count <= 0) return; - var group = BuildPipeline.GetBuildTargetGroup(target); - var parameters = new BundleBuildParameters(target, @group, buildWorkingDirectory) + var dependencyMapping = new Dictionary>>(); + var explicitLayout = new BundleExplictObjectLayout(); + ContentPipeline.BuildCallbacks.PostDependencyCallback = (buildParams, dependencyData) => ExtractDuplicateObjects(buildParams, dependencyInputData, explicitLayout, bundleNames, dependencyMapping); + var status = ContentPipeline.BuildAssetBundles(parameters, content, out IBundleBuildResults result, CreateTaskList(), explicitLayout); + PostBuildCallback?.Invoke(dependencyMapping); + foreach (var bundleName in bundleNames) + DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, buildWorkingDirectory + "/" + bundleName, "SubScenes/" + bundleName); + + foreach (var ssIter in subScenePaths) { - BundleCompression = BuildCompression.Uncompressed - }; + string headerArtifactPath = ssIter.Value; + Hash128 sceneGUID = ssIter.Key; - var status = ContentPipeline.BuildAssetBundles(parameters, content, out IBundleBuildResults result); + dependencyMapping.TryGetValue(sceneGUID, out var sceneDependencyData); + var tempPath = $"{buildWorkingDirectory}/{sceneGUID}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader)}"; - foreach (var bundleName in bundleNames) - RegisterFileCopy(buildWorkingDirectory + "/" + bundleName, outputStreamingAssetsDirectory + "/SubScenes/" + bundleName); + if (!BlobAssetReference.TryRead(headerArtifactPath, SceneMetaDataSerializeUtility.CurrentFileFormatVersion, out var sceneMetaDataRef)) + { + Debug.LogError($"Loading Entity Scene failed because the entity header file was an old version or doesn't exist: guid={headerArtifactPath} path={headerArtifactPath}"); + continue; + } + UpdateSceneMetaDataDependencies(ref sceneMetaDataRef, sceneDependencyData, tempPath); + sceneMetaDataRef.Dispose(); + var headerDestPath = EntityScenesPaths.RelativePathFolderFor(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1); + DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, tempPath, headerDestPath); + } var succeeded = status >= ReturnCode.Success; if (!succeeded) throw new InvalidOperationException($"BuildAssetBundles failed with status '{status}'."); } + public static Action>>> PostBuildCallback; + + static void UpdateSceneMetaDataDependencies(ref BlobAssetReference sceneMetaData, Dictionary> sceneDependencyData, string outPath) + { + var blob = new BlobBuilder(Allocator.Temp); + ref var root = ref blob.ConstructRoot(); + blob.Construct(ref root.Sections, sceneMetaData.Value.Sections.ToArray()); + blob.Construct(ref root.SceneSectionCustomMetadata, sceneMetaData.Value.SceneSectionCustomMetadata.ToArray()); + blob.AllocateString(ref root.SceneName, sceneMetaData.Value.SceneName.ToString()); + BlobBuilderArray> deps = blob.Allocate(ref root.Dependencies, sceneMetaData.Value.Sections.Length); + + if (sceneDependencyData != null) + { + for (int i = 0; i < deps.Length; i++) + { + var section = new SceneSection() + { + SceneGUID = sceneMetaData.Value.Sections[i].SceneGUID, + Section = sceneMetaData.Value.Sections[i].SubSectionIndex + }; + + if (sceneDependencyData.TryGetValue(section, out var bundleIds)) + blob.Construct(ref deps[i], bundleIds.ToArray()); + } + } + + BlobAssetReference.Write(blob, outPath, SceneMetaDataSerializeUtility.CurrentFileFormatVersion); + } + + static void DoCopy(Action RegisterFileCopy, string outputStreamingAssetsFolder, string src, string dst) + { + RegisterFileCopy(src, outputStreamingAssetsFolder + "/" + dst); +#if USE_ASSETBUNDLES_IN_EDITOR_PLAY_MODE + if (!Directory.Exists(UnityEngine.Application.streamingAssetsPath)) + Directory.CreateDirectory(UnityEngine.Application.streamingAssetsPath); + RegisterFileCopy(src, UnityEngine.Application.streamingAssetsPath + "/" + dst); +#endif + } + + public static SectionDependencyInfo CreateDependencyInfo(ObjectIdentifier[] objectIds, BuildTarget target, UnityEditor.Build.Player.TypeDB scriptInfo) + { + //TODO: cache this dependency data + var dependencies = ContentBuildInterface.GetPlayerDependenciesForObjects(objectIds, target, scriptInfo); + var depTypes = ContentBuildInterface.GetTypeForObjects(dependencies); + var paths = dependencies.Select(i => AssetDatabase.GUIDToAssetPath(i.guid.ToString())).ToArray(); + return new SectionDependencyInfo() { Dependencies = dependencies, Paths = paths, Types = depTypes }; + } + + public struct SectionDependencyInfo + { + public ObjectIdentifier[] Dependencies; + public Type[] Types; + public string[] Paths; + } + + static ReturnCode ExtractDuplicateObjects(IBuildParameters parameters, Dictionary dependencyInpuData, BundleExplictObjectLayout layout, HashSet bundleNames, Dictionary>> result) + { + var bundleLayout = new Dictionary>(); + CreateAssetLayoutData(dependencyInpuData, result, bundleLayout); + ExtractExplicitBundleLayout(bundleLayout, layout, bundleNames); + return ReturnCode.Success; + } + + static void ExtractExplicitBundleLayout(Dictionary> bundleLayout, BundleExplictObjectLayout layout, HashSet bundleNames) + { + foreach (var sectionIter in bundleLayout) + { + var bundleName = $"{sectionIter.Key}.bundle"; + foreach (var i in sectionIter.Value) + { + try + { layout.ExplicitObjectLocation.Add(i, bundleName); } + catch { Debug.LogError($"Trying to add bundle: '{bundleName}' current value '{layout.ExplicitObjectLocation[i]}' object type '{ContentBuildInterface.GetTypeForObject(i).Name}'"); }; + } + bundleNames.Add(bundleName); + } + } + + /// + /// Create bundle layout and depedendency data for subscene bundles + /// + /// Mapping of SceneSection to dependency info for that section. + /// Mapping of subscene id to mapping of section to bundle ids + /// Mapping of bundle ids to included objects + public static void CreateAssetLayoutData(Dictionary dependencyInputData, Dictionary>> dependencyResult, Dictionary> bundleLayoutResult) + { + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + + if (!ValidateInput(dependencyInputData, out var error)) + { + Debug.Log(error); + return; + } + + var depToSections = new Dictionary>(); + //for each subscene, collect all dependencies and map them to the scenes they are referenced by. + //also create a mapping from the subscene to all of its depedencies + foreach (var sectionIter in dependencyInputData) + { + foreach (var dependency in sectionIter.Value.Dependencies) + { + // Built In Resources we reference directly + if (dependency.guid == GUIDHelper.UnityBuiltinResources) + continue; + + if (!depToSections.TryGetValue(dependency, out List sectionList)) + { + sectionList = new List(); + depToSections.Add(dependency, sectionList); + } + sectionList.Add(sectionIter.Key); + } + } + + //convert each list of scenes into a hash + var objToSectionUsageHash = new Dictionary(); + foreach (var objIter in depToSections) + { + if (objIter.Value.Count <= 1) + continue; + + objToSectionUsageHash.Add(objIter.Key, HashingMethods.Calculate(objIter.Value).ToHash128()); + } + + if (objToSectionUsageHash.Count > 0) + { + //create mapping from scene hash to included dependencies + foreach (var objIter in objToSectionUsageHash) + { + if (!bundleLayoutResult.TryGetValue(objIter.Value, out var ids)) + bundleLayoutResult.Add(objIter.Value, ids = new List()); + ids.Add(objIter.Key); + } + + foreach (var sectionIter in dependencyInputData) + { + var bundleHashes = new HashSet(); + foreach (var dep in dependencyInputData[sectionIter.Key].Dependencies) + if (objToSectionUsageHash.TryGetValue(dep, out var sceneHash)) + bundleHashes.Add(sceneHash); + if (!dependencyResult.TryGetValue(sectionIter.Key.SceneGUID, out var sectionMap)) + dependencyResult.Add(sectionIter.Key.SceneGUID, sectionMap = new Dictionary>()); + sectionMap[sectionIter.Key] = bundleHashes.ToList(); + } + } + + sw.Stop(); + Debug.Log($"CreateAssetLayoutData time: {sw.Elapsed}"); + } + + public static bool ValidateInput(Dictionary dependencyInputData, out string firstError) + { + firstError = null; + if (dependencyInputData == null) + { + firstError = "NULL dependencyInputData."; + return false; + } + foreach (var sec in dependencyInputData) + { + if (!sec.Key.SceneGUID.IsValid) + { + firstError = "Invalid scene guid for section."; + return false; + } + if (sec.Key.Section < 0) + { + firstError = $"Scene {sec.Key.SceneGUID} - Invalid section index {sec.Key.Section}."; + return false; + } + if (sec.Value.Dependencies == null) + { + firstError = $"Scene {sec.Key.SceneGUID} - null Dependencies."; + return false; + } + if (sec.Value.Paths == null) + { + firstError = $"Scene {sec.Key.SceneGUID} - null Paths."; + return false; + } + if (sec.Value.Types == null) + { + firstError = $"Scene {sec.Key.SceneGUID} - null Types."; + return false; + } + if (sec.Value.Dependencies.Length != sec.Value.Paths.Length || sec.Value.Dependencies.Length != sec.Value.Types.Length) + { + firstError = $"Scene {sec.Key.SceneGUID} - Data length mismatch: Dependencies: {sec.Value.Dependencies.Length}, Types: {sec.Value.Types.Length}, Paths: {sec.Value.Paths.Length}."; + return false; + } + for (int i = 0; i < sec.Value.Dependencies.Length; i++) + { + if (sec.Value.Dependencies[i].guid.Empty()) + { + firstError = $"Scene {sec.Key.SceneGUID} - Dependencies[{i}] has invalid GUID, path='{sec.Value.Paths[i]}'."; + return false; + } + if (sec.Value.Types[i] == null) + { + firstError = $"Scene {sec.Key.SceneGUID} - Types[{i}] is NULL, path='{sec.Value.Paths[i]}'."; + return false; + } + if (string.IsNullOrEmpty(sec.Value.Paths[i])) + { + firstError = $"Scene {sec.Key.SceneGUID} - Paths[{i}] is NULL or empty."; + return false; + } + } + } + return true; + } + + public class UpdateBundlePacking : IBuildTask + { + public int Version { get { return 1; } } + +#pragma warning disable 649 + [InjectContext] + IBundleWriteData m_WriteData; + + [InjectContext(ContextUsage.In, true)] + IBundleExplictObjectLayout m_Layout; + + [InjectContext(ContextUsage.In)] + IDeterministicIdentifiers m_PackingMethod; +#pragma warning restore 649 + + public ReturnCode Run() + { + if (m_Layout != null) + { + var extractedBundlesToFileDependencies = new Dictionary>(); + foreach (var pair in m_Layout.ExplicitObjectLocation) + { + ObjectIdentifier objectID = pair.Key; + string bundleName = pair.Value; + string internalName = string.Format(CommonStrings.AssetBundleNameFormat, m_PackingMethod.GenerateInternalFileName(bundleName)); + foreach (var assetFilesPair in m_WriteData.AssetToFiles) + { + if (assetFilesPair.Value.Contains(internalName)) + { + if (!extractedBundlesToFileDependencies.TryGetValue(internalName, out var dependencies)) + { + extractedBundlesToFileDependencies.Add(internalName, dependencies = new HashSet()); + foreach (var afp in assetFilesPair.Value) + dependencies.Add(afp); + } + } + } + } + Dictionary fileToCommand = m_WriteData.WriteOperations.ToDictionary(x => x.Command.internalName, x => x.Command); + foreach (var pair in extractedBundlesToFileDependencies) + { + var refMap = m_WriteData.FileToReferenceMap[pair.Key]; + foreach (var fileDependency in pair.Value) + { + var cmd = fileToCommand[fileDependency]; + refMap.AddMappings(fileDependency, cmd.serializeObjects.ToArray()); + } + var cmd2 = fileToCommand[pair.Key]; + refMap.AddMappings(pair.Key, cmd2.serializeObjects.ToArray(), true); + } + } + return ReturnCode.Success; + } + } + + static IList CreateTaskList() + { + var taskList = DefaultBuildTasks.Create(DefaultBuildTasks.Preset.AssetBundleBuiltInShaderExtraction); + // Remove the shader task to use the DOTS dedupe pass only + taskList.Remove(taskList.First(x => x is CreateBuiltInShadersBundle)); + // Insert the dedupe dependency resolver task + taskList.Insert(taskList.IndexOf(taskList.First(x => x is GenerateSubAssetPathMaps)), new UpdateBundlePacking()); + return taskList; + } } } diff --git a/Unity.Scenes.Editor/SubSceneImporter.cs b/Unity.Scenes.Editor/SubSceneImporter.cs index 29a7ed89..fbd71dbe 100644 --- a/Unity.Scenes.Editor/SubSceneImporter.cs +++ b/Unity.Scenes.Editor/SubSceneImporter.cs @@ -5,6 +5,8 @@ using Unity.Entities; using Unity.Entities.Serialization; using Unity.Build; +using Unity.Build.DotsRuntime; +using Unity.Core.Compression; using UnityEditor; using UnityEditor.Build.Content; using UnityEditor.SceneManagement; @@ -16,7 +18,7 @@ namespace Unity.Scenes.Editor { - [ScriptedImporter(83, "extDontMatter")] + [ScriptedImporter(84, "extDontMatter")] [InitializeOnLoad] class SubSceneImporter : ScriptedImporter { @@ -92,6 +94,7 @@ public override void OnImportAsset(AssetImportContext ctx) try { ctx.DependsOnCustomDependency("EntityBinaryFileFormatVersion"); + ctx.DependsOnCustomDependency("SceneMetaDataFileFormatVersion"); ctx.DependsOnSourceAsset(EntitiesCacheUtility.globalEntitySceneDependencyPath); var sceneWithBuildConfiguration = SceneWithBuildConfigurationGUIDs.ReadFromFile(ctx.assetPath); @@ -129,8 +132,28 @@ public override void OnImportAsset(AssetImportContext ctx) settings.AssetImportContext = ctx; settings.FilterFlags = WorldSystemFilterFlags.HybridGameObjectConversion; + WriteEntitySceneSettings writeEntitySettings = new WriteEntitySceneSettings(); + if (config != null && config.TryGetComponent(out var profile)) + { + if (profile.UseNewPipeline) + { + if (config.TryGetComponent(out var rootAssembly)) + { + EditorSceneManager.SetActiveScene(scene); + writeEntitySettings.Codec = Codec.LZ4; + writeEntitySettings.IsDotsRuntime = true; + writeEntitySettings.BuildAssemblyCache = new BuildAssemblyCache() + { + BaseAssemblies = rootAssembly.RootAssembly.asset, + PlatformName = profile.Target.UnityPlatformName + }; + settings.FilterFlags = WorldSystemFilterFlags.DotsRuntimeGameObjectConversion; + } + } + } + var sectionRefObjs = new List(); - var sectionData = EditorEntityScenes.ConvertAndWriteEntityScene(scene, settings, sectionRefObjs); + var sectionData = EditorEntityScenes.ConvertAndWriteEntitySceneInternal(scene, settings, sectionRefObjs, writeEntitySettings); WriteAssetDependencyGUIDs(sectionRefObjs, sectionData, ctx); } diff --git a/Unity.Scenes.Editor/SubSceneInspector.cs b/Unity.Scenes.Editor/SubSceneInspector.cs index d129b489..4e517e62 100644 --- a/Unity.Scenes.Editor/SubSceneInspector.cs +++ b/Unity.Scenes.Editor/SubSceneInspector.cs @@ -19,10 +19,14 @@ class SubSceneInspector : UnityEditor.Editor string m_ConversionLog = ""; SceneAsset[] m_PreviousSceneAssets; + private SubScene[] _selectedSubscenes; private void OnEnable() { Undo.undoRedoPerformed += OnUndoRedoPerformed; + _selectedSubscenes = new SubScene[targets.Length]; + var targetsArray = targets; + targetsArray.CopyTo(_selectedSubscenes, 0); } private void OnDisable() @@ -103,75 +107,170 @@ void HandleChangedSceneAssetReferences() SceneHierarchyHooks.ReloadAllSceneHierarchies(); } - public override void OnInspectorGUI() - { - var subScene = target as SubScene; + static readonly GUIContent s_TmpContent = new GUIContent(); - if (!subScene.IsInMainStage()) + static Rect DrawButtonGridLabelAndGetFirstButtonRect(GUIContent label, int numButtons, out float spacing) + { + Rect controlRect; + if (EditorGUIUtility.wideMode) { - // In Prefab Mode and when selecting a Prefab Asset in the Project Browser we only show the inspector of data of the - // SubScene, and not the load/unload/edit/close buttons. - base.OnInspectorGUI(); - - EditorGUILayout.HelpBox($"Only Sub Scenes in the Main Stage can be loaded and unloaded.", MessageType.Info, true); - EditorGUILayout.Space(); - return; + controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + controlRect = EditorGUI.PrefixLabel(controlRect, label); } + else + { + EditorGUILayout.PrefixLabel(label); + ++EditorGUI.indentLevel; + controlRect = EditorGUI.IndentedRect( + EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight) + ); + --EditorGUI.indentLevel; + } + spacing = GUI.skin.button.margin.horizontal / 2f; + controlRect.width = (controlRect.width - spacing * (numButtons - 1)) / numButtons; + return controlRect; + } - var prevColor = subScene.HierarchyColor; - CachePreviousSceneAssetReferences(); - - base.OnInspectorGUI(); - - HandleChangedSceneAssetReferences(); - - if (subScene.HierarchyColor != prevColor) - SceneHierarchyHooks.ReloadAllSceneHierarchies(); - - var targetsArray = targets; - var subscenes = new SubScene[targetsArray.Length]; - targetsArray.CopyTo(subscenes, 0); - - GUILayout.BeginHorizontal(); - if (!SubSceneInspectorUtility.IsEditingAll(subscenes)) + private static void DrawOpenSubScenes(SubScene[] subScenes) + { + int numOpen = 0; + for (int i = 0; i < subScenes.Length; i++) { - GUI.enabled = SubSceneInspectorUtility.CanEditScene(subscenes); - if (GUILayout.Button("Edit")) + if (subScenes[i].IsLoaded) { - SubSceneInspectorUtility.EditScene(subscenes); + numOpen++; } } - else + + if (numOpen > 0) { - GUI.enabled = true; - if (GUILayout.Button("Close")) + GUILayout.Space(EditorGUIUtility.singleLineHeight); + GUILayout.BeginHorizontal(); + GUILayout.Label(Content.OpenSubScenesLabel, EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + GUILayout.Label($"{numOpen}"); + GUILayout.EndHorizontal(); + + for (int i = 0; i < subScenes.Length; i++) { - SubSceneInspectorUtility.CloseAndAskSaveIfUserWantsTo(subscenes); + var scene = subScenes[i]; + if (!scene.IsLoaded) + continue; + + s_TmpContent.text = scene.SceneName; + s_TmpContent.tooltip = scene.EditableScenePath; + var buttonRect = DrawButtonGridLabelAndGetFirstButtonRect(s_TmpContent, 3, out var spacing); + + // add empty space space so buttons are right-aligned + buttonRect.x += buttonRect.width + spacing; + using (new EditorGUI.DisabledScope(!scene.EditingScene.isDirty)) + { + if (GUI.Button(buttonRect, Content.SaveLabel)) + { + SubSceneInspectorUtility.SaveScene(scene); + } + } + buttonRect.x += buttonRect.width + spacing; + if (GUI.Button(buttonRect, Content.CloseLabel)) + { + SubSceneInspectorUtility.CloseAndAskSaveIfUserWantsTo(scene); + } } } + } - GUI.enabled = SubSceneInspectorUtility.IsDirty(subscenes); - if (GUILayout.Button("Save")) + private static bool DrawClosedSubScenes(SubSceneInspectorUtility.LoadableScene[] loadableScenes, SubScene[] subscenes) + { + if (World.DefaultGameObjectInjectionWorld != null && loadableScenes.Length != 0) { - SubSceneInspectorUtility.SaveScene(subscenes); - } - GUI.enabled = true; + GUILayout.Space(EditorGUIUtility.singleLineHeight); + var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; + + { + int numScenesLoaded = 0; + int numScenesImported = 0; + foreach (var scene in loadableScenes) + { + if (entityManager.HasComponent(scene.Scene)) + numScenesLoaded++; + if (IsSubsceneImported(scene.SubScene)) + numScenesImported++; + } + + if (EditorGUIUtility.wideMode) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(Content.ClosedSubScenesLabel, EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + GUILayout.Label(string.Format(Content.ClosedStatusString, numScenesLoaded, loadableScenes.Length, numScenesImported)); + GUILayout.EndHorizontal(); + } + else + { + GUILayout.Label(Content.ClosedSubScenesLabel, EditorStyles.boldLabel); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label(string.Format(Content.ClosedStatusString, numScenesLoaded, loadableScenes.Length, numScenesImported)); + GUILayout.EndHorizontal(); + } + } - GUILayout.EndHorizontal(); + if (loadableScenes.Length > 1) + { + GUILayout.BeginHorizontal(); + bool reimportRequested = GUILayout.Button(Content.ReimportAllLabel); - var scenes = SubSceneInspectorUtility.GetLoadableScenes(subscenes); + if (GUILayout.Button(Content.LoadAllLabel)) + { + foreach (var scene in loadableScenes) + { + if (!entityManager.HasComponent(scene.Scene)) + entityManager.AddComponentData(scene.Scene, new RequestSceneLoaded()); + } - GUILayout.Space(10); + EditorUpdateUtility.EditModeQueuePlayerLoopUpdate(); + } - if (World.DefaultGameObjectInjectionWorld != null) - { - var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; + if (GUILayout.Button(Content.UnloadAllLabel)) + { + foreach (var scene in loadableScenes) + { + if (entityManager.HasComponent(scene.Scene)) + entityManager.RemoveComponent(scene.Scene); + } + EditorUpdateUtility.EditModeQueuePlayerLoopUpdate(); + } - foreach (var scene in scenes) + GUILayout.EndHorizontal(); + + if (reimportRequested && EditorUtility.DisplayDialog(Content.ReimportAllSubScenes, Content.ReimportAllSubScenesDetails, Content.Yes, Content.No)) + SubSceneInspectorUtility.ForceReimport(subscenes); + + GUILayout.Space(EditorGUIUtility.standardVerticalSpacing); + } + + bool needsRepaint = false; + foreach (var scene in loadableScenes) { + s_TmpContent.text = scene.Name; + s_TmpContent.tooltip = scene.SubScene.EditableScenePath; + + var buttonRect = DrawButtonGridLabelAndGetFirstButtonRect(s_TmpContent, 3, out var spacing); + + if (!IsSubsceneImported(scene.SubScene)) + { + GUI.Label(buttonRect, Content.ImportingLabel); + needsRepaint = true; + } + else if (GUI.Button(buttonRect, Content.ReimportLabel)) + { + SubSceneInspectorUtility.ForceReimport(scene.SubScene); + } + + buttonRect.x += buttonRect.width + spacing; if (!entityManager.HasComponent(scene.Scene)) { - if (GUILayout.Button($"Load '{scene.Name}'")) + if (GUI.Button(buttonRect, Content.LoadLabel)) { entityManager.AddComponentData(scene.Scene, new RequestSceneLoaded()); EditorUpdateUtility.EditModeQueuePlayerLoopUpdate(); @@ -179,16 +278,62 @@ public override void OnInspectorGUI() } else { - if (GUILayout.Button($"Unload '{scene.Name}'")) + if (GUI.Button(buttonRect, Content.UnloadLabel)) { entityManager.RemoveComponent(scene.Scene); EditorUpdateUtility.EditModeQueuePlayerLoopUpdate(); } } + + buttonRect.x += buttonRect.width + spacing; + using (new EditorGUI.DisabledScope(!SubSceneInspectorUtility.CanEditScene(scene.SubScene))) + { + if (GUI.Button(buttonRect, Content.OpenLabel)) + { + SubSceneInspectorUtility.EditScene(scene.SubScene); + } + } } + + return needsRepaint; + } + + return false; + } + + public override void OnInspectorGUI() + { + var subScene = target as SubScene; + + if (!subScene.IsInMainStage()) + { + // In Prefab Mode and when selecting a Prefab Asset in the Project Browser we only show the inspector of data of the + // SubScene, and not the load/unload/edit/close buttons. + base.OnInspectorGUI(); + + EditorGUILayout.HelpBox($"Only Sub Scenes in the Main Stage can be loaded and unloaded.", MessageType.Info, true); + EditorGUILayout.Space(); + return; } - #if false + var prevColor = subScene.HierarchyColor; + CachePreviousSceneAssetReferences(); + + base.OnInspectorGUI(); + + HandleChangedSceneAssetReferences(); + + if (subScene.HierarchyColor != prevColor) + SceneHierarchyHooks.ReloadAllSceneHierarchies(); + + DrawOpenSubScenes(_selectedSubscenes); + var loadableScenes = SubSceneInspectorUtility.GetLoadableScenes(_selectedSubscenes); + if (DrawClosedSubScenes(loadableScenes, _selectedSubscenes)) + { + Repaint(); + } + +#if false // @TODO: TEMP for debugging if (GUILayout.Button("ClearWorld")) { @@ -219,13 +364,13 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); } - var uncleanHierarchyObject = SubSceneInspectorUtility.GetUncleanHierarchyObject(subscenes); + var uncleanHierarchyObject = SubSceneInspectorUtility.GetUncleanHierarchyObject(_selectedSubscenes); if (uncleanHierarchyObject != null) { EditorGUILayout.HelpBox($"Scene transform values are not applied to scenes child transforms. But {uncleanHierarchyObject.name} has an offset Transform.", MessageType.Warning, true); if (GUILayout.Button("Clear")) { - foreach (var scene in subscenes) + foreach (var scene in _selectedSubscenes) { scene.transform.localPosition = Vector3.zero; scene.transform.localRotation = Quaternion.identity; @@ -234,33 +379,27 @@ public override void OnInspectorGUI() } EditorGUILayout.Space(); } - if (SubSceneInspectorUtility.HasChildren(subscenes)) + if (SubSceneInspectorUtility.HasChildren(_selectedSubscenes)) { EditorGUILayout.HelpBox($"SubScenes can not have child game objects. Close the scene and delete the child game objects.", MessageType.Warning, true); } - GUILayout.Space(10); - if (CheckConversionLog(subScene)) - { - GUILayout.Label("Importing..."); - Repaint(); - } - else + if (targets.Length == 1) { - if (!SubSceneInspectorUtility.IsEditingAll(subscenes)) + GUILayout.Space(EditorGUIUtility.singleLineHeight); + if (CheckConversionLog(subScene)) { - if (GUILayout.Button("Reimport")) - { - SubSceneInspectorUtility.ForceReimport(subscenes); - } + GUILayout.Label("Importing..."); + Repaint(); } - } - if (m_ConversionLog.Length != 0) - { - GUILayout.Space(10); - GUILayout.Label("Conversion Log"); - GUILayout.TextArea(m_ConversionLog); + if (m_ConversionLog.Length != 0) + { + GUILayout.Space(EditorGUIUtility.singleLineHeight); + + GUILayout.Label("Conversion Log", EditorStyles.boldLabel); + GUILayout.TextArea(m_ConversionLog); + } } } @@ -284,6 +423,22 @@ static void DrawSubsceneBounds(SubScene scene, GizmoType gizmoType) SubSceneInspectorUtility.DrawSubsceneBounds(scene); } + static bool IsSubsceneImported(SubScene subScene) + { + foreach (var world in World.All) + { + var sceneSystem = world.GetExistingSystem(); + if (sceneSystem is null) + continue; + + var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, sceneSystem.BuildConfigurationGUID, ImportMode.NoImport); + if (!hash.IsValid) + return false; + } + + return true; + } + bool CheckConversionLog(SubScene subScene) { var pendingWork = false; @@ -324,5 +479,28 @@ bool CheckConversionLog(SubScene subScene) return pendingWork; } + + static class Content + { + public static readonly GUIContent OpenSubScenesLabel = EditorGUIUtility.TrTextContent("Open SubScenes"); + public static readonly GUIContent ClosedSubScenesLabel = EditorGUIUtility.TrTextContent("Closed SubScenes"); + public static readonly GUIContent CloseLabel = EditorGUIUtility.TrTextContent("Close"); + public static readonly GUIContent SaveLabel = EditorGUIUtility.TrTextContent("Save"); + public static readonly GUIContent LoadAllLabel = EditorGUIUtility.TrTextContent("Load All"); + public static readonly GUIContent UnloadAllLabel = EditorGUIUtility.TrTextContent("Unload All"); + public static readonly GUIContent ReimportAllLabel = EditorGUIUtility.TrTextContent("Reimport All"); + public static readonly GUIContent LoadLabel = EditorGUIUtility.TrTextContent("Load"); + public static readonly GUIContent UnloadLabel = EditorGUIUtility.TrTextContent("Unload"); + public static readonly GUIContent ReimportLabel = EditorGUIUtility.TrTextContent("Reimport"); + public static readonly GUIContent ImportingLabel = EditorGUIUtility.TrTextContent("Importing..."); + public static readonly GUIContent OpenLabel = EditorGUIUtility.TrTextContent("Open"); + public static readonly string ClosedStatusString = L10n.Tr("{0} / {1} loaded, {2} / {1} imported"); + public static readonly string ReimportAllSubScenes = L10n.Tr("Reimport All Selected SubScenes?"); + public static readonly string ReimportAllSubScenesDetails = + L10n.Tr( + "Reimporting all SubScenes could take a considerable amount of time. Do you really want to trigger a reimport?"); + public static readonly string Yes = L10n.Tr("Yes"); + public static readonly string No = L10n.Tr("No"); + } } } diff --git a/Unity.Scenes.Editor/SubSceneInspectorUtility.cs b/Unity.Scenes.Editor/SubSceneInspectorUtility.cs index a1bc516f..1a3c7767 100644 --- a/Unity.Scenes.Editor/SubSceneInspectorUtility.cs +++ b/Unity.Scenes.Editor/SubSceneInspectorUtility.cs @@ -63,6 +63,7 @@ public struct LoadableScene { public Entity Scene; public string Name; + public SubScene SubScene; } static NativeArray GetActiveWorldSections(World world, Hash128 sceneGUID) @@ -87,21 +88,23 @@ public static LoadableScene[] GetLoadableScenes(SubScene[] scenes) { var loadables = new List(); + var world = World.DefaultGameObjectInjectionWorld; foreach (var scene in scenes) { - foreach (var section in GetActiveWorldSections(World.DefaultGameObjectInjectionWorld, scene.SceneGUID)) + foreach (var section in GetActiveWorldSections(world, scene.SceneGUID)) { - var name = scene.SceneAsset != null ? scene.SceneAsset.name : "Missing Scene Asset"; - if (World.DefaultGameObjectInjectionWorld.EntityManager.HasComponent(section)) + if (world.EntityManager.HasComponent(section)) { - var sectionIndex = World.DefaultGameObjectInjectionWorld.EntityManager.GetComponentData(section).SubSectionIndex; + var name = scene.SceneAsset != null ? scene.SceneAsset.name : "Missing Scene Asset"; + var sectionIndex = world.EntityManager.GetComponentData(section).SubSectionIndex; if (sectionIndex != 0) name += $" Section: {sectionIndex}"; loadables.Add(new LoadableScene { Scene = section, - Name = name + Name = name, + SubScene = scene }); } } @@ -110,35 +113,27 @@ public static LoadableScene[] GetLoadableScenes(SubScene[] scenes) return loadables.ToArray(); } - public static void ForceReimport(SubScene[] scenes) + public static void ForceReimport(params SubScene[] scenes) { - foreach (var scene in scenes) + bool needRefresh = false; + foreach (var world in World.All) { - foreach (var world in World.All) + var sceneSystem = world.GetExistingSystem(); + if (sceneSystem != null) { - var sceneSystem = world.GetExistingSystem(); - if (sceneSystem != null) + foreach (var scene in scenes) { var guid = SceneWithBuildConfigurationGUIDs.Dirty(scene.SceneGUID, sceneSystem.BuildConfigurationGUID, out var requestRefresh); - if(requestRefresh) - AssetDatabase.Refresh(); + needRefresh |= requestRefresh; // Ignoring return as this is just being used to start an impot, we don't actually care about the hash result - AssetDatabaseCompatibility.GetArtifactHash(guid.ToString(), EntityScenesPaths.SubSceneImporterType, ImportMode.Asynchronous); + AssetDatabaseCompatibility.GetArtifactHash(guid.ToString(), + EntityScenesPaths.SubSceneImporterType, ImportMode.Asynchronous); } } } - } - - public static bool IsEditingAll(SubScene[] scenes) - { - foreach (var scene in scenes) - { - if (!scene.IsLoaded) - return false; - } - - return true; + if (needRefresh) + AssetDatabase.Refresh(); } public static bool CanEditScene(SubScene subScene) @@ -149,28 +144,6 @@ public static bool CanEditScene(SubScene subScene) return !subScene.IsLoaded; } - public static bool IsLoaded(SubScene[] scenes) - { - foreach (var subScene in scenes) - { - if (subScene.IsLoaded) - return true; - } - - return false; - } - - public static bool CanEditScene(SubScene[] scenes) - { - foreach (var subScene in scenes) - { - if (CanEditScene(subScene)) - return true; - } - - return false; - } - public static void EditScene(params SubScene[] scenes) { foreach (var subScene in scenes) @@ -187,7 +160,7 @@ public static void EditScene(params SubScene[] scenes) } } - public static void CloseAndAskSaveIfUserWantsTo(SubScene[] subScenes) + public static void CloseAndAskSaveIfUserWantsTo(params SubScene[] subScenes) { if (!Application.isPlaying) { @@ -218,28 +191,14 @@ public static void CloseAndAskSaveIfUserWantsTo(SubScene[] subScenes) } } - public static void SaveScene(SubScene[] subScenes) + public static void SaveScene(SubScene scene) { - foreach (var scene in subScenes) + if (scene.EditingScene.isLoaded && scene.EditingScene.isDirty) { - if (scene.EditingScene.isLoaded && scene.EditingScene.isDirty) - { - EditorSceneManager.SaveScene(scene.EditingScene); - } + EditorSceneManager.SaveScene(scene.EditingScene); } } - public static bool IsDirty(SubScene[] scenes) - { - foreach (var scene in scenes) - { - if (scene.EditingScene.isLoaded && scene.EditingScene.isDirty) - return true; - } - - return false; - } - public static MinMaxAABB GetActiveWorldMinMax(World world, UnityEngine.Object[] targets) { MinMaxAABB bounds = MinMaxAABB.Empty; diff --git a/Unity.Scenes.Editor/TypeDependencyCache.cs b/Unity.Scenes.Editor/TypeDependencyCache.cs index dfe1cc43..b39020d0 100644 --- a/Unity.Scenes.Editor/TypeDependencyCache.cs +++ b/Unity.Scenes.Editor/TypeDependencyCache.cs @@ -65,6 +65,11 @@ static unsafe TypeDependencyCache() UnityEngine.Hash128 fileFormatHash = default; HashUnsafeUtilities.ComputeHash128(&fileFormatVersion, sizeof(int), &fileFormatHash); UnityEditor.Experimental.AssetDatabaseExperimental.RegisterCustomDependency("EntityBinaryFileFormatVersion", fileFormatHash); + + int sceneFileFormatVersion = SceneMetaDataSerializeUtility.CurrentFileFormatVersion; + UnityEngine.Hash128 sceneFileFormatHash = default; + HashUnsafeUtilities.ComputeHash128(&sceneFileFormatVersion, sizeof(int), &sceneFileFormatHash); + UnityEditor.Experimental.AssetDatabaseExperimental.RegisterCustomDependency("SceneMetaDataFileFormatVersion", sceneFileFormatHash); } static void RegisterComponentTypes() diff --git a/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef b/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef index 5595ae2a..869a6590 100644 --- a/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef +++ b/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef @@ -11,7 +11,8 @@ "Unity.Properties", "Unity.Scenes", "Unity.ScriptableBuildPipeline.Editor", - "Unity.Serialization" + "Unity.Serialization", + "Unity.Build.DotsRuntime" ], "includePlatforms": [ "Editor" @@ -24,4 +25,4 @@ "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Unity.Scenes/AssetBundleManager.cs b/Unity.Scenes/AssetBundleManager.cs index d2b483cb..f0924755 100644 --- a/Unity.Scenes/AssetBundleManager.cs +++ b/Unity.Scenes/AssetBundleManager.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using Unity.Collections; using UnityEngine; @@ -16,6 +17,22 @@ public class SceneBundleHandle private AssetBundle _assetBundle; private readonly string _bundlePath; + public static bool UseAssetBundles + { + get + { +#if UNITY_EDITOR +#if USE_ASSETBUNDLES_IN_EDITOR_PLAY_MODE + if (Application.isPlaying) + return true; +#endif + return false; +#else + return true; +#endif + } + } + internal AssetBundle AssetBundle { get @@ -33,8 +50,8 @@ internal AssetBundle AssetBundle private SceneBundleHandle(string bundlePath) { _refCount = 0; - _assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(bundlePath); _bundlePath = bundlePath; + _assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(_bundlePath); } internal bool IsReady() @@ -63,16 +80,70 @@ internal void Release() ReleaseBundle(this); } } + //used by tests + internal static string[] GetLoadedBundlesPaths() + { + return LoadedBundles.Keys.ToArray(); + } internal void Retain() { Interlocked.Increment(ref _refCount); } - private static readonly Dictionary LoadedBundles = new Dictionary(); private static readonly ConcurrentDictionary UnloadingBundles = new ConcurrentDictionary(); + internal static List LoadSceneBundles(string mainBundlePath, NativeArray sharedBundles, bool blocking) + { + var bundles = new List(); + + if (sharedBundles.IsCreated) + { + for (int i = 0; i < sharedBundles.Length; i++) + { + var path = $"{Application.streamingAssetsPath}/{EntityScenesPaths.RelativePathFolderFor(sharedBundles[i], EntityScenesPaths.PathType.EntitiesSharedReferencesBundle, -1)}"; + bundles.Add(CreateOrRetainBundle(path)); + } + sharedBundles.Dispose(); + } + + if (!string.IsNullOrEmpty(mainBundlePath)) + { + bundles.Insert(0, CreateOrRetainBundle(mainBundlePath)); + } + + if (blocking) + { + foreach (var b in bundles) + { + var forceLoad = b.AssetBundle; + if (forceLoad == null) + { + Debug.LogWarning($"Failed to load asset bundle at path {b._bundlePath}"); + } + } + } + return bundles; + } - internal static SceneBundleHandle CreateOrRetainBundle(string bundlePath) + internal static bool CheckLoadingStatus(List bundles, ref string error) + { + if (bundles == null) + return true; + + foreach (var b in bundles) + { + if (!b.IsReady()) + return false; + if (b.AssetBundle == null) + { + error = $"Failed to load asset bundle at path {b._bundlePath}"; + return true; + } + } + return true; + } + + static SceneBundleHandle CreateOrRetainBundle(string bundlePath) { if (bundlePath == null) throw new InvalidOperationException("Bundle Path is null!"); @@ -82,9 +153,7 @@ internal static SceneBundleHandle CreateOrRetainBundle(string bundlePath) { // Check if it's about to be unloaded if (!UnloadingBundles.TryRemove(bundlePath, out assetBundleHandle)) - { assetBundleHandle = new SceneBundleHandle(bundlePath); - } LoadedBundles[bundlePath] = assetBundleHandle; } @@ -114,7 +183,7 @@ internal static void ProcessUnloadingBundles() { if (sceneBundleHandle.Value.IsReady()) { - sceneBundleHandle.Value.AssetBundle.Unload(true); + sceneBundleHandle.Value.AssetBundle?.Unload(true); UnloadingBundles.TryRemove(sceneBundleHandle.Key, out _); } diff --git a/Unity.Scenes/AsyncLoadSceneOperation.cs b/Unity.Scenes/AsyncLoadSceneOperation.cs index c0ef9f29..53315073 100644 --- a/Unity.Scenes/AsyncLoadSceneOperation.cs +++ b/Unity.Scenes/AsyncLoadSceneOperation.cs @@ -28,6 +28,8 @@ struct AsyncLoadSceneData public string ScenePath; public Codec Codec; public bool BlockUntilFullyLoaded; + public NativeArray Dependencies; + #if !UNITY_DISABLE_MANAGED_COMPONENTS public PostLoadCommandBuffer PostLoadCommandBuffer; #endif @@ -83,7 +85,11 @@ public void Dispose() } #if !UNITY_DOTSRUNTIME - _SceneBundleHandle?.Release(); + if (_SceneBundleHandles != null) + { + foreach (var h in _SceneBundleHandles) + h.Release(); + } #endif #if !UNITY_DISABLE_MANAGED_COMPONENTS @@ -141,7 +147,7 @@ public void Execute() #if !UNITY_DOTSRUNTIME ReferencedUnityObjects _ResourceObjRefs; - SceneBundleHandle _SceneBundleHandle; + List _SceneBundleHandles; AssetBundleRequest _AssetRequest; #endif @@ -182,11 +188,11 @@ public string ErrorStatus public Exception Exception => _LoadingException; #if !UNITY_DOTSRUNTIME - public SceneBundleHandle StealBundle() + public List StealBundles() { - SceneBundleHandle sceneBundleHandle = _SceneBundleHandle; - _SceneBundleHandle = null; - return sceneBundleHandle; + var tmp = _SceneBundleHandles; + _SceneBundleHandles = null; + return tmp; } #endif @@ -219,13 +225,19 @@ private void UpdateBlocking() #if !UNITY_DOTSRUNTIME if (_ExpectedObjectReferenceCount != 0) { + if (SceneBundleHandle.UseAssetBundles) + { + _SceneBundleHandles = SceneBundleHandle.LoadSceneBundles(_ResourcesPathObjRefs, _Data.Dependencies, true); + if(_SceneBundleHandles.Count > 0) + _ResourceObjRefs = _SceneBundleHandles[0].AssetBundle?.LoadAsset(Path.GetFileName(_ResourcesPathObjRefs)); + } + else + { #if UNITY_EDITOR - var resourceRequests = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(_ResourcesPathObjRefs); - _ResourceObjRefs = (ReferencedUnityObjects)resourceRequests[0]; -#else - _SceneBundleHandle = SceneBundleHandle.CreateOrRetainBundle(_ResourcesPathObjRefs); - _ResourceObjRefs = _SceneBundleHandle.AssetBundle.LoadAsset(Path.GetFileName(_ResourcesPathObjRefs)); + var resourceRequests = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(_ResourcesPathObjRefs); + _ResourceObjRefs = (ReferencedUnityObjects)resourceRequests[0]; #endif + } } #endif ScheduleSceneRead(); @@ -272,15 +284,19 @@ private void UpdateAsync() #if !UNITY_DOTSRUNTIME if (_ExpectedObjectReferenceCount != 0) { + if (SceneBundleHandle.UseAssetBundles) + { + _SceneBundleHandles = SceneBundleHandle.LoadSceneBundles(_ResourcesPathObjRefs, _Data.Dependencies, false); + _LoadingStatus = LoadingStatus.WaitingForAssetBundleLoad; + } + else + { #if UNITY_EDITOR - var resourceRequests = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(_ResourcesPathObjRefs); - _ResourceObjRefs = (ReferencedUnityObjects)resourceRequests[0]; - - _LoadingStatus = LoadingStatus.WaitingForResourcesLoad; -#else - _SceneBundleHandle = SceneBundleHandle.CreateOrRetainBundle(_ResourcesPathObjRefs); - _LoadingStatus = LoadingStatus.WaitingForAssetBundleLoad; + var resourceRequests = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(_ResourcesPathObjRefs); + _ResourceObjRefs = (ReferencedUnityObjects)resourceRequests[0]; + _LoadingStatus = LoadingStatus.WaitingForResourcesLoad; #endif + } } else { @@ -302,20 +318,18 @@ private void UpdateAsync() // Once async asset bundle load is done, we can read the asset if (_LoadingStatus == LoadingStatus.WaitingForAssetBundleLoad) { - if (!_SceneBundleHandle.IsReady()) - return; - - if (!_SceneBundleHandle.AssetBundle) + string error = null; + if (SceneBundleHandle.CheckLoadingStatus(_SceneBundleHandles, ref error)) { - _LoadingFailure = $"Failed to load Asset Bundle '{_ResourcesPathObjRefs}'"; - _LoadingStatus = LoadingStatus.Completed; - return; + if (!string.IsNullOrEmpty(error)) + { + _LoadingFailure = error; + _LoadingStatus = LoadingStatus.Completed; + } + var fileName = Path.GetFileName(_ResourcesPathObjRefs); + _AssetRequest = _SceneBundleHandles[0].AssetBundle.LoadAssetAsync(fileName); + _LoadingStatus = LoadingStatus.WaitingForAssetLoad; } - - var fileName = Path.GetFileName(_ResourcesPathObjRefs); - - _AssetRequest = _SceneBundleHandle.AssetBundle.LoadAssetAsync(fileName); - _LoadingStatus = LoadingStatus.WaitingForAssetLoad; } // Once async asset bundle load is done, we can read the asset @@ -326,7 +340,7 @@ private void UpdateAsync() if (!_AssetRequest.asset) { - _LoadingFailure = $"Failed to load Asset '{Path.GetFileName(_ResourcesPathObjRefs)}'"; + _LoadingFailure = $"Failed to load Asset '{_ResourcesPathObjRefs}'"; _LoadingStatus = LoadingStatus.Completed; return; } diff --git a/Unity.Scenes/EntityScenesPaths.cs b/Unity.Scenes/EntityScenesPaths.cs index 4a0bfa26..0ae97356 100644 --- a/Unity.Scenes/EntityScenesPaths.cs +++ b/Unity.Scenes/EntityScenesPaths.cs @@ -17,21 +17,22 @@ internal class EntityScenesPaths public static Type SubSceneImporterType = null; #if !UNITY_DOTSRUNTIME + public static Hash128 BuiltinShadersBundleHash = new Hash128(0, 0, 0, 1); internal static readonly string PersistentDataPath = Application.persistentDataPath; internal static readonly string StreamingAssetsPath = Application.streamingAssetsPath; #else internal static readonly string PersistentDataPath = "Data"; internal static readonly string StreamingAssetsPath = "Data"; #endif - public enum PathType { EntitiesUnityObjectReferences, - EntitiesUnitObjectReferencesBundle, + EntitiesUnityObjectReferencesBundle, EntitiesAssetDependencyGUIDs, EntitiesBinary, EntitiesConversionLog, - EntitiesHeader + EntitiesHeader, + EntitiesSharedReferencesBundle } public static string GetExtension(PathType pathType) @@ -41,13 +42,14 @@ public static string GetExtension(PathType pathType) // these must all be lowercase case PathType.EntitiesUnityObjectReferences: return "asset"; case PathType.EntitiesBinary: return "entities"; - case PathType.EntitiesUnitObjectReferencesBundle: return "bundle"; + case PathType.EntitiesUnityObjectReferencesBundle: return "bundle"; case PathType.EntitiesHeader: return "entityheader"; case PathType.EntitiesConversionLog: return "conversionlog"; + case PathType.EntitiesSharedReferencesBundle: return "bundle"; case PathType.EntitiesAssetDependencyGUIDs: return "dependencies"; } - throw new ArgumentException("Unknown PathType"); + throw new ArgumentException($"Unknown PathType {pathType}"); } #if UNITY_EDITOR @@ -79,10 +81,10 @@ public static string GetLoadPathFromArtifactPaths(string[] paths, PathType type, public static string GetLoadPath(Hash128 sceneGUID, PathType type, int sectionIndex) { - return StreamingAssetsPath + "/" + RelativePathInStreamingAssetsFolderFor(sceneGUID, type, sectionIndex); + return StreamingAssetsPath + "/" + RelativePathFolderFor(sceneGUID, type, sectionIndex); } - public static string RelativePathInStreamingAssetsFolderFor(Hash128 sceneGUID, PathType type, int sectionIndex) + public static string RelativePathFolderFor(Hash128 sceneGUID, PathType type, int sectionIndex) { var extension = GetExtension(type); switch (type) @@ -90,9 +92,13 @@ public static string RelativePathInStreamingAssetsFolderFor(Hash128 sceneGUID, P case PathType.EntitiesBinary: return $"SubScenes/{sceneGUID}.{sectionIndex}.{extension}"; case PathType.EntitiesHeader: + case PathType.EntitiesConversionLog: return $"SubScenes/{sceneGUID}.{extension}"; case PathType.EntitiesUnityObjectReferences: - return $"SubScenes/{sceneGUID}.{sectionIndex}.bundle"; + case PathType.EntitiesUnityObjectReferencesBundle: + return $"SubScenes/{sceneGUID}.{sectionIndex}.{extension}"; + case PathType.EntitiesSharedReferencesBundle: + return $"SubScenes/{sceneGUID}.{extension}"; default: throw new ArgumentException(); } @@ -107,7 +113,7 @@ public static string GetLiveLinkCachePath(UnityEngine.Hash128 targetHash, PathTy return $"{PersistentDataPath}/{k_LiveLinkCacheDir}/{targetHash}.{extension}"; case PathType.EntitiesBinary: case PathType.EntitiesUnityObjectReferences: - case PathType.EntitiesUnitObjectReferencesBundle: + case PathType.EntitiesUnityObjectReferencesBundle: return $"{PersistentDataPath}/{k_LiveLinkCacheDir}/{targetHash}.{sectionIndex}.{extension}"; default: return ""; diff --git a/Unity.Scenes/LiveLink/LiveLinkPlayerAssetRefreshSystem.cs b/Unity.Scenes/LiveLink/LiveLinkPlayerAssetRefreshSystem.cs index c0626c32..46261b71 100644 --- a/Unity.Scenes/LiveLink/LiveLinkPlayerAssetRefreshSystem.cs +++ b/Unity.Scenes/LiveLink/LiveLinkPlayerAssetRefreshSystem.cs @@ -409,7 +409,7 @@ unsafe bool IsSubSceneAvailable(in ResolvedSubSceneID subSceneId) if (sceneMetaData.Sections[i].ObjectReferenceCount != 0) { - var assetBundlePath = EntityScenesPaths.GetLiveLinkCachePath(subSceneId.TargetHash, EntityScenesPaths.PathType.EntitiesUnitObjectReferencesBundle, sectionIndex); + var assetBundlePath = EntityScenesPaths.GetLiveLinkCachePath(subSceneId.TargetHash, EntityScenesPaths.PathType.EntitiesUnityObjectReferencesBundle, sectionIndex); if (!File.Exists(assetBundlePath)) { LiveLinkMsg.LogInfo($"Missing Entity AssetBundle file! {assetBundlePath}"); diff --git a/Unity.Scenes/LiveLink/LiveLinkResolveSceneReferenceSystem.cs b/Unity.Scenes/LiveLink/LiveLinkResolveSceneReferenceSystem.cs index 42f8d215..852a4ec2 100644 --- a/Unity.Scenes/LiveLink/LiveLinkResolveSceneReferenceSystem.cs +++ b/Unity.Scenes/LiveLink/LiveLinkResolveSceneReferenceSystem.cs @@ -78,8 +78,9 @@ void ResolveScene(Entity sceneEntity, ref SceneReference scene, RequestSceneLoad EntityManager.AddComponentData(sectionEntity, new SceneBoundingVolume { Value = sceneMetaData.Sections[i].BoundingVolume }); EntityManager.AddComponentData(sectionEntity, new SceneEntityReference {SceneEntity = sceneEntity}); +#if !UNITY_DOTSPLAYER var sectionPath = new ResolvedSectionPath(); - var hybridPath = EntityScenesPaths.GetLiveLinkCachePath(artifactHash, EntityScenesPaths.PathType.EntitiesUnitObjectReferencesBundle, sectionIndex); + var hybridPath = EntityScenesPaths.GetLiveLinkCachePath(artifactHash, EntityScenesPaths.PathType.EntitiesUnityObjectReferencesBundle, sectionIndex); var scenePath = EntityScenesPaths.GetLiveLinkCachePath(artifactHash, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex); sectionPath.ScenePath.SetString(scenePath); @@ -92,6 +93,7 @@ void ResolveScene(Entity sceneEntity, ref SceneReference scene, RequestSceneLoad var buffer = EntityManager.GetBuffer(sceneEntity); buffer.Add(new ResolvedSectionEntity { SectionEntity = sectionEntity }); +#endif var linkedEntityGroup = EntityManager.GetBuffer(sceneEntity); linkedEntityGroup.Add(new LinkedEntityGroup { Value = sectionEntity }); } diff --git a/Unity.Scenes/ResolveSceneReferenceSystem.cs b/Unity.Scenes/ResolveSceneReferenceSystem.cs index c2a61fb0..fb533e32 100644 --- a/Unity.Scenes/ResolveSceneReferenceSystem.cs +++ b/Unity.Scenes/ResolveSceneReferenceSystem.cs @@ -13,6 +13,11 @@ public struct ResolvedSectionEntity : IBufferElementData public Entity SectionEntity; } + struct BundleElementData : IBufferElementData + { + public Hash128 BundleId; + } + struct SceneEntityReference : IComponentData { public Entity SceneEntity; @@ -39,7 +44,8 @@ struct SceneSectionCustomMetadata struct SceneMetaData { public BlobArray Sections; - public BlobString SceneName; + public BlobString SceneName; + public BlobArray> Dependencies; public BlobArray> SceneSectionCustomMetadata; } @@ -49,7 +55,7 @@ public struct DisableSceneResolveAndLoad : IComponentData static class SceneMetaDataSerializeUtility { - public static readonly int CurrentFileFormatVersion = 1; + public static readonly int CurrentFileFormatVersion = 3; } #if UNITY_EDITOR diff --git a/Unity.Scenes/ResolveSceneSectionUtility.cs b/Unity.Scenes/ResolveSceneSectionUtility.cs index 471c6699..5ff64bcb 100644 --- a/Unity.Scenes/ResolveSceneSectionUtility.cs +++ b/Unity.Scenes/ResolveSceneSectionUtility.cs @@ -38,22 +38,33 @@ internal static void RequestLoadAndPollSceneMetaData(EntityManager EntityManager EntityManager.AddComponentData(sceneEntity, new SceneMetaDataLoaded() { Success = sceneHeaderStatus == AsyncOp.Status.Success }); } #endif - - public static bool ResolveSceneSections(EntityManager EntityManager, Entity sceneEntity, Hash128 sceneGUID, RequestSceneLoaded requestSceneLoaded, Hash128 artifactHash) + public unsafe static bool ResolveSceneSections(EntityManager EntityManager, Entity sceneEntity, Hash128 sceneGUID, RequestSceneLoaded requestSceneLoaded, Hash128 artifactHash) { // Resolve first (Even if the file doesn't exist we want to stop continously trying to load the section) EntityManager.AddBuffer(sceneEntity); - EntityManager.AddBuffer(sceneEntity).Add(sceneEntity); - - #if UNITY_EDITOR - EntityManager.AddComponentData(sceneEntity, new ResolvedSceneHash { ArtifactHash = artifactHash }); + var sceneHeaderPath = ""; +#if UNITY_EDITOR + string[] paths = null; +#endif - AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out var paths); + bool useStreamingAssetPath = true; +#if !UNITY_DOTSRUNTIME + useStreamingAssetPath = SceneBundleHandle.UseAssetBundles; +#endif + if (useStreamingAssetPath) + { + sceneHeaderPath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1); + } + else + { +#if UNITY_EDITOR + AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out paths); + sceneHeaderPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesHeader); +#endif + } - var sceneHeaderPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesHeader); - #else - var sceneHeaderPath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1); - #endif + EntityManager.AddComponentData(sceneEntity, new ResolvedSceneHash { ArtifactHash = artifactHash }); + EntityManager.AddBuffer(sceneEntity).Add(sceneEntity); // @TODO: AsyncReadManager currently crashes with empty path. // It should be possible to remove this after that is fixed. @@ -106,10 +117,10 @@ public static bool ResolveSceneSections(EntityManager EntityManager, Entity scen ref var sceneMetaData = ref sceneMetaDataRef.Value; - #if UNITY_EDITOR +#if UNITY_EDITOR var sceneName = sceneMetaData.SceneName.ToString(); EntityManager.SetName(sceneEntity, $"Scene: {sceneName}"); - #endif +#endif // If auto-load is enabled var loadSections = (requestSceneLoaded.LoadFlags & SceneLoadFlags.DisableAutoLoad) == 0; @@ -118,9 +129,9 @@ public static bool ResolveSceneSections(EntityManager EntityManager, Entity scen { var sectionEntity = EntityManager.CreateEntity(); var sectionIndex = sceneMetaData.Sections[i].SubSectionIndex; - #if UNITY_EDITOR +#if UNITY_EDITOR EntityManager.SetName(sectionEntity, $"SceneSection: {sceneName} ({sectionIndex})"); - #endif +#endif if (loadSections) { @@ -131,14 +142,22 @@ public static bool ResolveSceneSections(EntityManager EntityManager, Entity scen EntityManager.AddComponentData(sectionEntity, new SceneBoundingVolume { Value = sceneMetaData.Sections[i].BoundingVolume }); EntityManager.AddComponentData(sectionEntity, new SceneEntityReference { SceneEntity = sceneEntity }); + var hybridPath = ""; + var scenePath = ""; var sectionPath = new ResolvedSectionPath(); - #if !UNITY_EDITOR - var hybridPath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex); - var scenePath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex); - #else - var scenePath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex); - var hybridPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex); - #endif + + if (useStreamingAssetPath) + { + hybridPath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesUnityObjectReferencesBundle, sectionIndex); + scenePath = EntityScenesPaths.GetLoadPath(sceneGUID, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex); + } + else + { +#if UNITY_EDITOR + scenePath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex); + hybridPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex); +#endif + } sectionPath.ScenePath.SetString(scenePath); if (hybridPath != null) @@ -146,15 +165,25 @@ public static bool ResolveSceneSections(EntityManager EntityManager, Entity scen EntityManager.AddComponentData(sectionEntity, sectionPath); - #if UNITY_EDITOR +#if UNITY_EDITOR if (EntityManager.HasComponent(sceneEntity)) EntityManager.AddComponentObject(sectionEntity, EntityManager.GetComponentObject(sceneEntity)); - #endif +#endif AddSectionMetadataComponents(sectionEntity, ref sceneMetaData.SceneSectionCustomMetadata[i], EntityManager); var buffer = EntityManager.GetBuffer(sceneEntity); buffer.Add(new ResolvedSectionEntity { SectionEntity = sectionEntity }); + if (sceneMetaData.Dependencies.Length > 0) + { + ref var deps = ref sceneMetaData.Dependencies[i]; + if (deps.Length > 0) + { + var bundleSet = EntityManager.AddBuffer(sectionEntity); + bundleSet.ResizeUninitialized(deps.Length); + UnsafeUtility.MemCpy(bundleSet.GetUnsafePtr(), deps.GetUnsafePtr(), sizeof(Hash128) * deps.Length); + } + } var linkedEntityGroup = EntityManager.GetBuffer(sceneEntity); linkedEntityGroup.Add(new LinkedEntityGroup { Value = sectionEntity }); } diff --git a/Unity.Scenes/SceneSectionBundleTag.cs b/Unity.Scenes/SceneSectionBundleTag.cs index 5ff3ea58..7865ff0d 100644 --- a/Unity.Scenes/SceneSectionBundleTag.cs +++ b/Unity.Scenes/SceneSectionBundleTag.cs @@ -1,5 +1,6 @@ #if !UNITY_DOTSRUNTIME using System; +using System.Collections.Generic; using Unity.Entities; namespace Unity.Scenes @@ -7,33 +8,51 @@ namespace Unity.Scenes [Serializable] internal struct SceneSectionBundle : ISharedComponentData, IEquatable, IRefCounted { - private SceneBundleHandle _sceneBundleHandle; + private List _sceneBundleHandles; - public SceneSectionBundle(SceneBundleHandle bundle) + public SceneSectionBundle(IEnumerable bundles) { - _sceneBundleHandle = bundle; + _sceneBundleHandles = new List(bundles); } public void Release() { - _sceneBundleHandle?.Release(); - _sceneBundleHandle = null; + foreach (var b in _sceneBundleHandles) + b.Release(); } public void Retain() { - _sceneBundleHandle?.Retain(); + foreach (var b in _sceneBundleHandles) + b.Retain(); } public bool Equals(SceneSectionBundle other) { - return _sceneBundleHandle == other._sceneBundleHandle; + if (GetHashCode() != other.GetHashCode()) + return false; + + if (other._sceneBundleHandles == null) + return false; + + if (_sceneBundleHandles.Count != other._sceneBundleHandles.Count) + return false; + + for (int i = 0; i < _sceneBundleHandles.Count; i++) + if (!_sceneBundleHandles[i].Equals(other._sceneBundleHandles[i])) + return false; + + return true; } public override int GetHashCode() { + if (_sceneBundleHandles == null || _sceneBundleHandles.Count == 0) + return 0; + int hash = 0; - if (!ReferenceEquals(_sceneBundleHandle, null)) hash ^= _sceneBundleHandle.GetHashCode(); + for (int i = 0; i < _sceneBundleHandles.Count; i++) + hash ^= _sceneBundleHandles[i].GetHashCode(); return hash; } } diff --git a/Unity.Scenes/SceneSectionStreamingSystem.cs b/Unity.Scenes/SceneSectionStreamingSystem.cs index 8fd2515b..d09e683b 100644 --- a/Unity.Scenes/SceneSectionStreamingSystem.cs +++ b/Unity.Scenes/SceneSectionStreamingSystem.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; @@ -365,7 +366,7 @@ UpdateLoadOperationResult UpdateLoadOperation(AsyncLoadSceneOperation operation, if (MoveEntities(streamingManager, sceneEntity)) { #if !UNITY_DOTSRUNTIME - var bundle = operation.StealBundle(); + var bundles = operation.StealBundles(); #endif if (EntityManager.HasComponent(sceneEntity)) @@ -376,8 +377,12 @@ UpdateLoadOperationResult UpdateLoadOperation(AsyncLoadSceneOperation operation, } #if !UNITY_DOTSRUNTIME - if (bundle != null) - EntityManager.AddSharedComponentData(sceneEntity, new SceneSectionBundle(bundle)); + if (bundles != null) + { + EntityManager.AddSharedComponentData(sceneEntity, new SceneSectionBundle(bundles)); + foreach (var b in bundles) + b.Release(); + } #endif return UpdateLoadOperationResult.Completed; @@ -565,6 +570,12 @@ AsyncLoadSceneOperation CreateAsyncLoadSceneOperation(EntityManager dstManager, var entitiesBinaryPath = sectionData.ScenePath.ToString(); var resourcesPath = sectionData.HybridPath.ToString(); + NativeArray dependencies = default; + if (EntityManager.HasComponent(entity)) + { + var depBuffer = EntityManager.GetBuffer(entity); + dependencies = new NativeArray(depBuffer.AsNativeArray().Reinterpret(), Allocator.Persistent); + } #if !UNITY_DISABLE_MANAGED_COMPONENTS PostLoadCommandBuffer postLoadCommandBuffer = null; @@ -594,6 +605,7 @@ AsyncLoadSceneOperation CreateAsyncLoadSceneOperation(EntityManager dstManager, ResourcesPathObjRefs = resourcesPath, EntityManager = dstManager, BlockUntilFullyLoaded = blockUntilFullyLoaded, + Dependencies = dependencies, #if !UNITY_DISABLE_MANAGED_COMPONENTS PostLoadCommandBuffer = postLoadCommandBuffer #endif diff --git a/Unity.Scenes/SubScene.cs b/Unity.Scenes/SubScene.cs index 92f722e6..a996e017 100644 --- a/Unity.Scenes/SubScene.cs +++ b/Unity.Scenes/SubScene.cs @@ -98,6 +98,9 @@ public Scene EditingScene } } + /// + /// Returns whether the subscene is open for editing. + /// public bool IsLoaded { get { return EditingScene.isLoaded; } @@ -167,6 +170,10 @@ internal bool IsInMainStage() void OnEnable() { + // Do not move the default initialization below the early out, it's important for the world to exist + // if the Subscene gets assigned later, otherwise the change won't trigger an import/conversion. + DefaultWorldInitialization.DefaultLazyEditModeInitialize(); + #if UNITY_EDITOR WarnIfNeeded(); @@ -180,7 +187,6 @@ void OnEnable() return; #endif - DefaultWorldInitialization.DefaultLazyEditModeInitialize(); AddSceneEntities(); } diff --git a/package.json b/package.json index f60051b8..16102ee0 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { "name": "com.unity.entities", "displayName": "Entities", - "version": "0.13.0-preview.24", + "version": "0.14.0-preview.18", "unity": "2020.1", "unityRelease": "0b15", "dependencies": { "com.unity.burst": "1.3.2", - "com.unity.collections": "0.11.0-preview.17", + "com.unity.collections": "0.12.0-preview.13", "com.unity.properties": "1.3.1-preview", "com.unity.mathematics": "1.1.0", - "com.unity.test-framework.performance": "2.0.8-preview", + "com.unity.test-framework.performance": "2.3.1-preview", "com.unity.serialization": "1.3.1-preview", - "nuget.mono-cecil": "0.1.5-preview", - "com.unity.jobs": "0.4.0-preview.18", - "com.unity.scriptablebuildpipeline": "1.6.4-preview", - "com.unity.platforms": "0.6.0-preview.1" + "com.unity.nuget.mono-cecil": "0.1.6-preview.2", + "com.unity.jobs": "0.5.0-preview.14", + "com.unity.scriptablebuildpipeline": "1.9.0", + "com.unity.platforms": "0.7.0-preview.8" }, "description": "The Entities package provides a modern Entity Component System (ECS) implementation with a basic set of systems and components made for Unity.", "keywords": [ @@ -23,11 +23,11 @@ "unity" ], "upmCi": { - "footprint": "cdbaf799bdbe3a54e850d2e02c53312c581a58c6" + "footprint": "fe8b7cc16119ac3dfdb5ac89755fa8ce1325f0df" }, "repository": { "url": "https://github.com/Unity-Technologies/dots.git", "type": "git", - "revision": "f6a2f99f4405c73f3879226e60c4f6194dac8fd2" + "revision": "bf6393d24822a1f9622218c9bb8155099aeabb81" } }